From 09582ab6a895a7a763dc24812bc30ac7238cb957 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 13 Mar 2024 09:05:23 -0300 Subject: [PATCH 01/15] WIP --- src/i18n/en/index.js | 16 + src/i18n/es/index.js | 16 + src/pages/Item/ItemList.vue | 550 ++++++++++++++++++++++++++++++- src/router/modules/item.js | 4 +- src/stores/useNavigationStore.js | 1 + 5 files changed, 584 insertions(+), 3 deletions(-) diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 94d552be3..6c4cc34f3 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -1164,6 +1164,22 @@ export default { warehouseText: 'Calculated on the warehouse of { warehouseName }', itemDiary: 'Item diary', }, + list: { + id: 'Identifier', + grouping: 'Grouping', + description: 'Description', + stems: 'Stems', + category: 'Category', + type: 'Type', + intrastat: 'Intrastat', + size: 'Size', + origin: 'Origin', + userName: 'Buyer', + weightByPiece: 'Weight/Piece', + multiplier: 'Multiplier', + producer: 'Producer', + landed: 'Landed', + }, }, components: { topbar: {}, diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index bcd780f5b..4ad9a1d5c 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -1164,6 +1164,22 @@ export default { warehouseText: 'Calculado sobre el almacén de { warehouseName }', itemDiary: 'Registro de compra-venta', }, + list: { + id: 'Identificador', + grouping: 'Grouping', + description: 'Descripción', + stems: 'Tallos', + category: 'Reino', + type: 'Tipo', + intrastat: 'Intrastat', + size: 'Medida', + origin: 'Origen', + weightByPiece: 'Peso (gramos)/tallo', + userName: 'Comprador', + multiplier: 'Multiplicador', + producer: 'Productor', + landed: 'F. entrega', + }, }, components: { topbar: {}, diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 49a5dbb64..14549296b 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -1 +1,549 @@ -<template>Item list</template> +<script setup> +import { onMounted, ref, computed, reactive, onUnmounted } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRouter } from 'vue-router'; + +import FetchData from 'components/FetchData.vue'; +import FetchedTags from 'components/ui/FetchedTags.vue'; +import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue'; +import VnInput from 'src/components/common/VnInput.vue'; +import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; +import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue'; +import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; + +import { useStateStore } from 'stores/useStateStore'; +import { toDate, toCurrency } from 'src/filters'; +import { useSession } from 'composables/useSession'; +import { dashIfEmpty } from 'src/filters'; +import { useArrayData } from 'composables/useArrayData'; + +const router = useRouter(); +const session = useSession(); +const token = session.getToken(); +const stateStore = useStateStore(); +const { t } = useI18n(); + +const itemTypesOptions = ref([]); +const originsOptions = ref([]); +const itemFamiliesOptions = ref([]); +// const intrastatOptions = ref([]); +const packagingsOptions = ref([]); +const visibleColumns = ref([]); +const allColumnNames = ref([]); + +const exprBuilder = (param, value) => { + switch (param) { + case 'category': + return { 'ic.name': value }; + case 'buyerFk': + return { 'it.workerFk': value }; + case 'grouping': + return { 'b.grouping': value }; + case 'packing': + return { 'b.packing': value }; + case 'origin': + return { 'ori.code': value }; + case 'typeFk': + return { 'i.typeFk': value }; + case 'intrastat': + return { 'intr.description': value }; + case 'name': + return { 'i.name': { like: `%${value}%` } }; + case 'producer': + return { 'pr.name': { like: `%${value}%` } }; + case 'id': + case 'size': + case 'subname': + case 'isActive': + case 'weightByPiece': + case 'stemMultiplier': + case 'stems': + return { [`i.${param}`]: value }; + } +}; + +const params = reactive({}); +const arrayData = useArrayData('ItemList', { + url: 'Items/filter', + order: ['isActive DESC', 'name', 'id'], + exprBuilder: exprBuilder, +}); +const store = arrayData.store; +const rows = computed(() => store.data); + +// const getInputEvents = (col) => { +// return col.columnFilter.type === 'select' +// ? { 'update:modelValue': () => applyColumnFilter(col) } +// : { +// 'keyup.enter': () => applyColumnFilter(col), +// }; +// }; + +const columns = computed(() => [ + { + label: '', + name: 'picture', + align: 'left', + }, + { + label: t('item.list.id'), + name: 'id', + field: 'id', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + }, + { + label: t('item.list.grouping'), + field: 'grouping', + name: 'grouping', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + format: (val) => dashIfEmpty(val), + }, + { + label: t('item.list.packing'), + field: 'packing', + name: 'packing', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + format: (val) => dashIfEmpty(val), + }, + { + label: t('globals.description'), + field: 'description', + name: 'description', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + // format: (val) => dashIfEmpty(val), + }, + { + label: t('item.list.stems'), + field: 'stems', + name: 'stems', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + }, + { + label: t('item.list.size'), + field: 'size', + name: 'size', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + }, + { + label: t('item.list.type'), + field: 'typeName', + name: 'typeName', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnSelectFilter, + // type: 'select', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // options: itemTypesOptions.value, + // 'option-value': 'code', + // 'option-label': 'code', + // dense: true, + // }, + // }, + }, + + { + label: t('item.list.category'), + field: 'category', + name: 'category', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnSelectFilter, + // type: 'select', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // options: intrastatOptions.value, + // 'option-value': 'description', + // 'option-label': 'description', + // dense: true, + // }, + // }, + }, + + { + label: t('item.list.intrastat'), + field: 'intrastat', + name: 'intrastat', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnSelectFilter, + // type: 'select', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // options: originsOptions.value, + // 'option-value': 'code', + // 'option-label': 'code', + // dense: true, + // }, + // }, + }, + { + label: t('item.list.origin'), + field: 'origin', + name: 'origin', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + }, + { + label: t('item.list.userName'), + field: 'userName', + name: 'userName', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnSelectFilter, + // type: 'select', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // options: itemFamiliesOptions.value, + // 'option-value': 'code', + // 'option-label': 'code', + // dense: true, + // }, + // }, + }, + { + label: t('item.list.weightByPiece'), + field: 'weightByPiece', + name: 'weightByPiece', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + format: (val) => dashIfEmpty(val), + }, + { + label: t('item.list.multiplier'), + field: 'stemMultiplier', + name: 'stemMultiplier', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + format: (val) => dashIfEmpty(val), + }, + { + label: t('entry.latestBuys.isActive'), + field: 'isActive', + name: 'isActive', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + }, + { + label: t('item.list.producer'), + field: 'producer', + name: 'producer', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + format: (val) => dashIfEmpty(val), + }, + { + label: t('item.list.landed'), + field: 'landed', + name: 'landed', + align: 'left', + sortable: true, + // columnFilter: { + // component: VnInput, + // type: 'text', + // filterValue: null, + // event: getInputEvents, + // attrs: { + // dense: true, + // }, + // }, + format: (val) => dashIfEmpty(toDate(val)), + }, + { + label: '', + name: 'actions', + align: 'left', + }, +]); + +const redirectToItemCreate = () => { + // router.push({ name: 'EntryBuys', params: { id: entryFk } }); +}; + +const redirectToItemSummary = () => { + // router.push({ name: 'EntryBuys', params: { id: entryFk } }); +}; + +// const applyColumnFilter = async (col) => { +// try { +// params[col.field] = col.columnFilter.filterValue; +// await arrayData.addFilter({ params }); +// } catch (err) { +// console.error('Error applying column filter', err); +// } +// }; + +onMounted(async () => { + stateStore.rightDrawer = true; + const filteredColumns = columns.value.filter((col) => col.name !== 'picture'); + allColumnNames.value = filteredColumns.map((col) => col.name); + await arrayData.fetch({ append: false }); +}); + +onUnmounted(() => (stateStore.rightDrawer = false)); +</script> + +<template> + <!-- <FetchData + url="ItemTypes" + :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }" + auto-load + @on-fetch="(data) => (itemTypesOptions = data)" + /> --> + <!-- <FetchData + url="Origins" + :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }" + auto-load + @on-fetch="(data) => (originsOptions = data)" + /> --> + <!-- <FetchData + url="ItemFamilies" + :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }" + auto-load + @on-fetch="(data) => (itemFamiliesOptions = data)" + /> --> + <!-- <FetchData + url="Packagings" + :filter="{ fields: ['id'], order: 'id ASC', limit: 30 }" + auto-load + @on-fetch="(data) => (packagingsOptions = data)" + /> --> + <QToolbar class="bg-vn-dark justify-end"> + <div id="st-data"> + <TableVisibleColumns + :all-columns="allColumnNames" + table-code="itemsIndex" + labels-traductions-path="item.list" + @on-config-saved="visibleColumns = ['picture', ...$event]" + /> + </div> + <QSpace /> + <div id="st-actions"></div> + </QToolbar> + <!-- <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> + <QScrollArea class="fit text-grey-8"> + <EntryLatestBuysFilter data-key="EntryLatestBuys" :tags="tags" /> + </QScrollArea> + </QDrawer> --> + <QPage class="column items-center q-pa-md"> + <QTable + :rows="rows" + :columns="columns" + row-key="id" + :pagination="{ rowsPerPage: 0 }" + class="full-width q-mt-md" + :visible-columns="visibleColumns" + :no-data-label="t('globals.noResults')" + @row-click="(_, row) => redirectToItemSummary(row.id)" + > + <!-- <template #top-row="{ cols }"> + <QTr> + <QTd /> + <QTd + v-for="(col, index) in cols" + :key="index" + style="max-width: 100px" + > + <component + :is="col.columnFilter.component" + v-if="col.name !== 'picture'" + v-model="col.columnFilter.filterValue" + v-bind="col.columnFilter.attrs" + v-on="col.columnFilter.event(col)" + dense + /> + </QTd> + </QTr> + </template> --> + <template #body-cell-picture="{ row }"> + <QTd> + <QImg + :src="`/api/Images/catalog/50x50/${row.id}/download?access_token=${token}`" + spinner-color="primary" + :ratio="1" + height="50px" + width="50px" + class="image" + /> + </QTd> + </template> + <template #body-cell-id="{ row }"> + <QTd @click.stop> + <QBtn flat color="primary"> + {{ row.id }} + </QBtn> + <ItemDescriptorProxy :id="row.id" /> + </QTd> + </template> + <template #body-cell-userName="{ row }"> + <QTd @click.stop> + <QBtn flat color="primary" dense> + {{ row.userName }} + </QBtn> + <WorkerDescriptorProxy :id="row.buyerFk" /> + </QTd> + </template> + <template #body-cell-description="{ row }"> + <QTd class="col"> + <span>{{ row.name }} {{ row.subName }}</span> + <fetched-tags :item="row" :max-length="6" /> + </QTd> + </template> + <template #body-cell-isActive="{ row }"> + <QTd> + <QCheckbox :model-value="!!row.isActive" disable /> + </QTd> + </template> + <template #body-cell-actions="{}"> + <QTd> + <QIcon + @click.stop="viewSummary($props.id, CustomerSummary)" + class="q-ml-sm" + color="primary" + name="vn:clone" + size="sm" + > + <QTooltip> + {{ t('Preview') }} + </QTooltip> + </QIcon> + <QIcon + @click.stop="viewSummary($props.id, CustomerSummary)" + class="q-ml-md" + color="primary" + name="preview" + size="sm" + > + <QTooltip> + {{ t('Preview') }} + </QTooltip> + </QIcon> + </QTd> + </template> + </QTable> + <QPageSticky :offset="[20, 20]"> + <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" /> + <QTooltip> + {{ t('New item') }} + </QTooltip> + </QPageSticky> + </QPage> +</template> + +<i18n> +es: + New item: Nuevo artículo +</i18n> diff --git a/src/router/modules/item.js b/src/router/modules/item.js index d3462e15c..700278617 100644 --- a/src/router/modules/item.js +++ b/src/router/modules/item.js @@ -10,7 +10,7 @@ export default { component: RouterView, redirect: { name: 'ItemMain' }, menus: { - main: [], + main: ['ItemList'], card: [], }, children: [ @@ -18,7 +18,7 @@ export default { path: '', name: 'ItemMain', component: () => import('src/pages/Item/ItemMain.vue'), - redirect: { name: 'Itemlist' }, + redirect: { name: 'ItemList' }, children: [ { path: 'list', diff --git a/src/stores/useNavigationStore.js b/src/stores/useNavigationStore.js index afd3af0c0..f075301f6 100644 --- a/src/stores/useNavigationStore.js +++ b/src/stores/useNavigationStore.js @@ -7,6 +7,7 @@ import routes from 'src/router/modules'; export const useNavigationStore = defineStore('navigationStore', () => { const modules = [ + 'item', 'shelving', 'order', 'customer', From aa2db28edab94ee3f0da0771c3e71337697399d6 Mon Sep 17 00:00:00 2001 From: carlosfonseca <carlos.fonseca@mindshore.io> Date: Fri, 22 Mar 2024 08:30:54 -0500 Subject: [PATCH 02/15] Se crea submodulo mis documentos en worker --- src/css/app.scss | 5 + src/i18n/en/index.js | 5 +- src/i18n/es/index.js | 5 +- src/pages/Worker/Card/WorkerDms.vue | 164 +++++++++++ .../Worker/components/WorkerDmsActions.vue | 96 +++++++ .../Worker/components/WorkerDmsCreate.vue | 262 ++++++++++++++++++ .../Worker/components/WorkerDmsDelete.vue | 82 ++++++ src/pages/Worker/components/WorkerDmsEdit.vue | 246 ++++++++++++++++ src/router/modules/worker.js | 31 ++- 9 files changed, 891 insertions(+), 5 deletions(-) create mode 100644 src/pages/Worker/Card/WorkerDms.vue create mode 100644 src/pages/Worker/components/WorkerDmsActions.vue create mode 100644 src/pages/Worker/components/WorkerDmsCreate.vue create mode 100644 src/pages/Worker/components/WorkerDmsDelete.vue create mode 100644 src/pages/Worker/components/WorkerDmsEdit.vue diff --git a/src/css/app.scss b/src/css/app.scss index 3baa60c9b..c52cdb5d2 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -85,6 +85,11 @@ select:-webkit-autofill { color: $white; } +.card-width { + max-width: 800px; + width: 100%; +} + .vn-card { background-color: var(--vn-section-color); color: var(--vn-text-color); diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 80744b83d..197e94582 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -837,6 +837,7 @@ export default { workerCreate: 'New worker', department: 'Department', pda: 'PDA', + dms: 'My documentation', }, list: { name: 'Name', @@ -955,7 +956,7 @@ export default { roadmap: 'Roadmap', summary: 'Summary', basicData: 'Basic Data', - stops: 'Stops' + stops: 'Stops', }, }, roadmap: { @@ -963,7 +964,7 @@ export default { roadmap: 'Roadmap', summary: 'Summary', basicData: 'Basic Data', - stops: 'Stops' + stops: 'Stops', }, }, route: { diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 37ba8da85..61896a283 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -837,6 +837,7 @@ export default { workerCreate: 'Nuevo trabajador', department: 'Departamentos', pda: 'PDA', + dms: 'Mi documentación', }, list: { name: 'Nombre', @@ -955,7 +956,7 @@ export default { roadmap: 'Troncales', summary: 'Resumen', basicData: 'Datos básicos', - stops: 'Paradas' + stops: 'Paradas', }, }, roadmap: { @@ -963,7 +964,7 @@ export default { roadmap: 'Troncales', summary: 'Resumen', basicData: 'Datos básicos', - stops: 'Paradas' + stops: 'Paradas', }, }, route: { diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue new file mode 100644 index 000000000..144e3edee --- /dev/null +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -0,0 +1,164 @@ +<script setup> +import { computed, ref, watch } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRoute, useRouter } from 'vue-router'; + +import { downloadFile } from 'src/composables/downloadFile'; +import { toDateHour } from 'src/filters'; + +import FetchData from 'components/FetchData.vue'; +import WorkerDmsActions from '../components/WorkerDmsActions.vue'; + +const { t } = useI18n(); +const route = useRoute(); +const router = useRouter(); + +const workerDmsRef = ref(null); +const rows = ref([]); + +const filter = { where: { worker: route.params.id }, order: ['dmsFk DESC'], limit: 20 }; + +const columns = computed(() => [ + { + align: 'left', + field: 'dmsFk', + label: t('Id'), + name: 'id', + }, + { + align: 'left', + field: 'hardCopyNumber', + label: t('Order'), + name: 'order', + }, + { + align: 'left', + field: 'reference', + label: t('Reference'), + name: 'reference', + }, + { + align: 'left', + field: 'description', + label: t('Description'), + name: 'description', + }, + { + align: 'left', + field: 'hasFile', + label: t('Original'), + name: 'original', + }, + { + align: 'left', + field: 'file', + label: t('File'), + name: 'file', + }, + { + align: 'left', + field: 'created', + label: t('Created'), + name: 'created', + format: (value) => toDateHour(value), + }, + { + align: 'right', + field: 'actions', + label: '', + name: 'actions', + }, +]); + +watch( + () => route.params.id, + (newValue) => { + filter.where.worker = newValue; + setData(); + } +); + +const setData = () => { + workerDmsRef.value.fetch(); +}; + +const toWorkerDmsCreate = () => { + router.push({ name: 'WorkerDmsCreate' }); +}; +</script> + +<template> + <FetchData + ref="workerDmsRef" + :filter="filter" + @on-fetch="(data) => (rows = data)" + auto-load + :url="`WorkerDms/${route.params.id}/filter`" + /> + + <QPage class="column items-center q-pa-md"> + <QTable + :columns="columns" + :pagination="{ rowsPerPage: 12 }" + :rows="rows" + class="full-width q-mt-md" + row-key="id" + v-if="rows?.length" + > + <template #body-cell-order="{ row }"> + <QTd> + <QChip class="chip-color" square>{{ row.hardCopyNumber }}</QChip> + </QTd> + </template> + + <template #body-cell-original="{ row }"> + <QTd> + <QCheckbox :model-value="row.hasFile === 1" disable /> + </QTd> + </template> + + <template #body-cell-file="{ row }"> + <QTd> + <QBtn @click.stop="downloadFile(row.dmsFk)" color="blue" flat no-caps> + {{ row.file }} + </QBtn> + </QTd> + </template> + + <template #body-cell-actions="{ row }"> + <QTd> + <WorkerDmsActions :id="row.dmsFk" :promise="setData" /> + </QTd> + </template> + </QTable> + + <h5 class="flex justify-center color-vn-label" v-else> + {{ t('globals.noResults') }} + </h5> + </QPage> + + <QPageSticky :offset="[18, 18]"> + <QBtn @click.stop="toWorkerDmsCreate()" color="primary" fab icon="add" /> + <QTooltip> + {{ t('Upload file') }} + </QTooltip> + </QPageSticky> +</template> + +<style scoped> +.chip-color { + background-color: var(--vn-label); +} +</style> + +<i18n> +es: + Id: Id + Order: Orden + Reference: Referencia + Description: Descripción + File: Archivo + Original: Original + Created: Fecha creación + Upload file: Subir fichero +</i18n> diff --git a/src/pages/Worker/components/WorkerDmsActions.vue b/src/pages/Worker/components/WorkerDmsActions.vue new file mode 100644 index 000000000..9a8e4816e --- /dev/null +++ b/src/pages/Worker/components/WorkerDmsActions.vue @@ -0,0 +1,96 @@ +<script setup> +import { useI18n } from 'vue-i18n'; +import { useRoute, useRouter } from 'vue-router'; + +import { useQuasar } from 'quasar'; + +import { downloadFile } from 'src/composables/downloadFile'; + +import WorkerDmsDelete from 'src/pages/Worker/components/WorkerDmsDelete.vue'; + +const { t } = useI18n(); +const quasar = useQuasar(); +const route = useRoute(); +const router = useRouter(); + +const $props = defineProps({ + id: { + type: Number, + required: true, + }, + promise: { + type: Function, + required: true, + }, +}); + +const setDownloadFile = () => downloadFile($props.id); + +const toWorkerDmsEdit = () => { + router.push({ + name: 'WorkerDmsEdit', + params: { + id: route.params.id, + dmsId: $props.id, + }, + }); +}; + +const showWorkerDmsDeleteDialog = () => { + quasar.dialog({ + component: WorkerDmsDelete, + componentProps: { + id: $props.id, + promise: setData, + }, + }); +}; + +const setData = () => { + $props.promise(); +}; +</script> + +<template> + <div> + <QIcon + @click.stop="setDownloadFile" + color="primary" + name="cloud_download" + size="sm" + > + <QTooltip> + {{ t('actionFile', { action: t('globals.download') }) }} + </QTooltip> + </QIcon> + <QIcon + @click.stop="toWorkerDmsEdit" + class="q-ml-md" + color="primary" + name="edit" + size="sm" + > + <QTooltip> + {{ t('actionFile', { action: t('globals.edit') }) }} + </QTooltip> + </QIcon> + <QIcon + @click.stop="showWorkerDmsDeleteDialog" + class="q-ml-md" + color="primary" + name="delete" + size="sm" + > + <QTooltip> + {{ t('actionFile', { action: t('globals.remove') }) }} + </QTooltip> + </QIcon> + </div> +</template> + +<i18n> +en: + actionFile: '{action} file' +es: + actionFile: '{action} fichero' +</i18n> diff --git a/src/pages/Worker/components/WorkerDmsCreate.vue b/src/pages/Worker/components/WorkerDmsCreate.vue new file mode 100644 index 000000000..d38e044ca --- /dev/null +++ b/src/pages/Worker/components/WorkerDmsCreate.vue @@ -0,0 +1,262 @@ +<script setup> +import { onBeforeMount, ref, watch } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRoute, useRouter } from 'vue-router'; + +import axios from 'axios'; + +import { useState } from 'src/composables/useState'; +import { useValidator } from 'src/composables/useValidator'; +import useNotify from 'src/composables/useNotify'; + +import FetchData from 'components/FetchData.vue'; +import VnRow from 'components/ui/VnRow.vue'; +import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; +import VnInput from 'src/components/common/VnInput.vue'; + +const { notify } = useNotify(); +const { t } = useI18n(); +const { validate } = useValidator(); +const route = useRoute(); +const router = useRouter(); +const state = useState(); +const user = state.getUser(); + +const filterFindOne = { where: { code: 'paymentsLaw' } }; +const filterCompanies = { order: ['code'] }; +const filterWarehouses = { order: ['name'] }; + +const inputFileRef = ref(); +const client = ref({}); +const findOne = ref([]); +const allowedContentTypes = ref([]); +const optionsCompanies = ref([]); +const optionsWarehouses = ref([]); +const optionsDmsTypes = ref([]); +const isLoading = ref(false); +const dms = ref({ + hasFile: false, +}); + +onBeforeMount(() => { + const { companyFk, warehouseFk } = user.value; + dms.value.reference = route.params.id; + dms.value.companyId = companyFk; + dms.value.warehouseId = warehouseFk; +}); + +watch([client, findOne], ([newClient, newFindOne]) => { + dms.value.description = t('clientFileDescription', { + dmsTypeName: newFindOne.name?.toUpperCase(), + clientName: newClient.name?.toUpperCase(), + clientId: newClient.id, + }); + dms.value.dmsTypeId = newFindOne.id; +}); + +const saveData = async () => { + try { + const formData = new FormData(); + const files = dms.value.files; + + if (files && files.length > 0) { + for (let file of files) { + formData.append(file.name, file); + } + dms.value.hasFileAttached = true; + + const url = `Workers/${route.params.id}/uploadFile`; + await axios.post(url, formData, { + params: dms.value, + }); + notify('globals.dataSaved', 'positive'); + toWorkerDms(); + } + } catch (error) { + notify(error.message, 'negative'); + } +}; + +const toWorkerDms = () => { + router.push({ name: 'WorkerDms' }); +}; +</script> + +<template> + <fetch-data + @on-fetch="(data) => (client = data)" + auto-load + :url="`Clients/${route.params.id}/getCard`" + /> + <fetch-data + :filter="filterFindOne" + @on-fetch="(data) => (findOne = data)" + auto-load + url="DmsTypes/findOne" + /> + <fetch-data + @on-fetch="(data) => (allowedContentTypes = data)" + auto-load + url="DmsContainers/allowedContentTypes" + /> + <fetch-data + :filter="filterCompanies" + @on-fetch="(data) => (optionsCompanies = data)" + auto-load + url="Companies" + /> + <fetch-data + :filter="filterWarehouses" + @on-fetch="(data) => (optionsWarehouses = data)" + auto-load + url="Warehouses" + /> + <fetch-data + :filter="filterWarehouses" + @on-fetch="(data) => (optionsDmsTypes = data)" + auto-load + url="DmsTypes" + /> + + <Teleport to="#st-actions"> + <QBtnGroup push class="q-gutter-x-sm"> + <QBtn + :disabled="isLoading" + :label="t('globals.cancel')" + :loading="isLoading" + @click="toWorkerDms" + color="primary" + flat + icon="close" + /> + <QBtn + :disabled="isLoading" + :label="t('globals.save')" + :loading="isLoading" + @click.stop="saveData" + color="primary" + icon="save" + /> + </QBtnGroup> + </Teleport> + + <div class="full-width flex justify-center"> + <QCard class="card-width q-pa-lg"> + <QCardSection> + <QForm> + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <VnInput + :label="t('Reference')" + clearable + v-model="dms.reference" + /> + </div> + <div class="col"> + <VnSelectFilter + :label="t('Company')" + :options="optionsCompanies" + :rules="validate('entry.companyFk')" + option-label="code" + option-value="id" + v-model="dms.companyId" + /> + </div> + </VnRow> + + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <VnSelectFilter + :label="t('Warehouse')" + :options="optionsWarehouses" + option-label="name" + option-value="id" + v-model="dms.warehouseId" + /> + </div> + <div class="col"> + <VnSelectFilter + :label="t('Type')" + :options="optionsDmsTypes" + option-label="name" + option-value="id" + v-model="dms.dmsTypeId" + /> + </div> + </VnRow> + + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <VnInput + :label="t('Description')" + :rules="validate('route.description')" + clearable + type="textarea" + v-model="dms.description" + /> + </div> + </VnRow> + + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <QFile + ref="inputFileRef" + class="required" + :label="t('File')" + v-model="dms.files" + multiple + :accept="allowedContentTypes.join(',')" + clearable + clear-icon="close" + > + <template #append> + <QBtn + icon="vn:attach" + flat + round + padding="xs" + @click="inputFileRef.pickFiles()" + > + <QTooltip> + {{ t('Select a file') }} + </QTooltip> + </QBtn> + <QBtn icon="info" flat round padding="xs"> + <QTooltip max-width="30rem"> + {{ + `${t( + 'Allowed content types' + )}: ${allowedContentTypes.join(', ')}` + }} + </QTooltip> + </QBtn> + </template> + </QFile> + </div> + </VnRow> + + <QCheckbox + :label="t('Generate identifier for original file')" + v-model="dms.hasFile" + /> + </QForm> + </QCardSection> + </QCard> + </div> +</template> + +<i18n> +en: + clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}' +es: + Reference: Referencia + Company: Empresa + Warehouse: Almacén + Type: Tipo + Description: Descripción + clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}' + File: Fichero + Select a file: Selecciona un fichero + Allowed content types: Tipos de archivo permitidos + Generate identifier for original file: Generar identificador para archivo original +</i18n> diff --git a/src/pages/Worker/components/WorkerDmsDelete.vue b/src/pages/Worker/components/WorkerDmsDelete.vue new file mode 100644 index 000000000..5e1b98b24 --- /dev/null +++ b/src/pages/Worker/components/WorkerDmsDelete.vue @@ -0,0 +1,82 @@ +<script setup> +import { ref } from 'vue'; +import { useI18n } from 'vue-i18n'; + +import { useDialogPluginComponent } from 'quasar'; +import axios from 'axios'; + +import useNotify from 'src/composables/useNotify'; + +const $props = defineProps({ + id: { + type: Number, + required: true, + }, + promise: { + type: Function, + required: true, + }, +}); + +const { dialogRef } = useDialogPluginComponent(); +const { notify } = useNotify(); +const { t } = useI18n(); + +const closeButton = ref(null); + +const isLoading = ref(false); + +const deleteDms = async () => { + isLoading.value = true; + try { + await axios.post(`WorkerDms/${$props.id}/removeFile`); + if ($props.promise) await $props.promise(); + notify('globals.dataDeleted', 'positive'); + } catch (error) { + notify(error.message, 'negative'); + } finally { + closeButton.value.click(); + isLoading.value = false; + } +}; +</script> + +<template> + <QDialog ref="dialogRef"> + <QCard class="q-pa-md q-mb-md"> + <span ref="closeButton" class="row justify-end close-icon" v-close-popup> + <QIcon name="close" size="sm" /> + </span> + + <QCardSection> + <div class="mt-1 text-h6">{{ t('This file will be deleted') }}</div> + <div>{{ t('Are you sure you want to continue?') }}</div> + </QCardSection> + + <QCardActions class="flex justify-end"> + <QBtn + :disabled="isLoading" + :label="t('globals.cancel')" + :loading="isLoading" + class="q-mr-xl" + color="primary" + flat + v-close-popup + /> + <QBtn + :disabled="isLoading" + :label="t('globals.save')" + :loading="isLoading" + @click.stop="deleteDms" + color="primary" + /> + </QCardActions> + </QCard> + </QDialog> +</template> + +<i18n> +es: + This file will be deleted: Este fichero va a ser borrado + Are you sure you want to continue?: ¿Seguro que quieres continuar? +</i18n> diff --git a/src/pages/Worker/components/WorkerDmsEdit.vue b/src/pages/Worker/components/WorkerDmsEdit.vue new file mode 100644 index 000000000..8e84c6326 --- /dev/null +++ b/src/pages/Worker/components/WorkerDmsEdit.vue @@ -0,0 +1,246 @@ +<script setup> +import { ref } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRoute, useRouter } from 'vue-router'; + +import axios from 'axios'; + +import { useValidator } from 'src/composables/useValidator'; +import useNotify from 'src/composables/useNotify'; + +import FetchData from 'components/FetchData.vue'; +import VnRow from 'components/ui/VnRow.vue'; +import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; +import VnInput from 'src/components/common/VnInput.vue'; + +const { notify } = useNotify(); +const { t } = useI18n(); +const { validate } = useValidator(); +const route = useRoute(); +const router = useRouter(); + +const filterCompanies = { order: ['code'] }; +const filterWarehouses = { order: ['name'] }; + +const inputFileRef = ref(); +const allowedContentTypes = ref([]); +const optionsCompanies = ref([]); +const optionsWarehouses = ref([]); +const optionsDmsTypes = ref([]); +const isLoading = ref(false); +const dms = ref({ + hasFile: true, +}); + +const setCurrentDms = (data) => { + dms.value.reference = data.reference; + dms.value.companyId = data.companyFk; + dms.value.warehouseId = data.warehouseFk; + dms.value.dmsTypeId = data.dmsTypeFk; + dms.value.description = data.description; + dms.value.hasFile = data.hasFile; +}; + +const saveData = async () => { + try { + const formData = new FormData(); + console.log(formData); + const files = dms.value.files; + console.log(files); + + dms.value.hasFileAttached = files ? true : false; + const url = `dms/${route.params.dmsId}/updateFile`; + + if (files && files.length > 0) { + for (let file of files) { + formData.append(file.name, file); + } + + await axios.post(url, formData, { + params: dms.value, + }); + notify('globals.dataSaved', 'positive'); + } else { + await axios.post(url, dms.value); + } + + toWorkerDms(); + } catch (error) { + notify(error.message, 'negative'); + } +}; + +const toWorkerDms = () => { + router.push({ name: 'WorkerDms' }); +}; +</script> + +<template> + <fetch-data :url="`Dms/${route.params.dmsId}`" @on-fetch="setCurrentDms" auto-load /> + <fetch-data + @on-fetch="(data) => (allowedContentTypes = data)" + auto-load + url="DmsContainers/allowedContentTypes" + /> + <fetch-data + :filter="filterCompanies" + @on-fetch="(data) => (optionsCompanies = data)" + auto-load + url="Companies" + /> + <fetch-data + :filter="filterWarehouses" + @on-fetch="(data) => (optionsWarehouses = data)" + auto-load + url="Warehouses" + /> + <fetch-data + :filter="filterWarehouses" + @on-fetch="(data) => (optionsDmsTypes = data)" + auto-load + url="DmsTypes" + /> + + <Teleport to="#st-actions"> + <QBtnGroup push class="q-gutter-x-sm"> + <QBtn + :disabled="isLoading" + :label="t('globals.cancel')" + :loading="isLoading" + @click="toWorkerDms" + color="primary" + flat + icon="close" + /> + <QBtn + :disabled="isLoading" + :label="t('globals.save')" + :loading="isLoading" + @click.stop="saveData" + color="primary" + icon="save" + /> + </QBtnGroup> + </Teleport> + + <div class="full-width flex justify-center"> + <QCard class="card-width q-pa-lg"> + <QCardSection> + <QForm> + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <VnInput + :label="t('Reference')" + clearable + v-model="dms.reference" + /> + </div> + <div class="col"> + <VnSelectFilter + :label="t('Company')" + :options="optionsCompanies" + :rules="validate('entry.companyFk')" + option-label="code" + option-value="id" + v-model="dms.companyId" + /> + </div> + </VnRow> + + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <VnSelectFilter + :label="t('Warehouse')" + :options="optionsWarehouses" + option-label="name" + option-value="id" + v-model="dms.warehouseId" + /> + </div> + <div class="col"> + <VnSelectFilter + :label="t('Type')" + :options="optionsDmsTypes" + option-label="name" + option-value="id" + v-model="dms.dmsTypeId" + /> + </div> + </VnRow> + + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <VnInput + :label="t('Description')" + :rules="validate('route.description')" + clearable + type="textarea" + v-model="dms.description" + /> + </div> + </VnRow> + + <VnRow class="row q-gutter-md q-mb-md"> + <div class="col"> + <QFile + ref="inputFileRef" + class="required" + :label="t('File')" + v-model="dms.files" + multiple + :accept="allowedContentTypes.join(',')" + clearable + clear-icon="close" + > + <template #append> + <QBtn + icon="vn:attach" + flat + round + padding="xs" + @click="inputFileRef.pickFiles()" + > + <QTooltip> + {{ t('Select a file') }} + </QTooltip> + </QBtn> + <QBtn icon="info" flat round padding="xs"> + <QTooltip max-width="30rem"> + {{ + `${t( + 'Allowed content types' + )}: ${allowedContentTypes.join(', ')}` + }} + </QTooltip> + </QBtn> + </template> + </QFile> + </div> + </VnRow> + + <QCheckbox + :label="t('Generate identifier for original file')" + v-model="dms.hasFile" + disable + /> + </QForm> + </QCardSection> + </QCard> + </div> +</template> + +<i18n> +en: + clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}' +es: + Reference: Referencia + Company: Empresa + Warehouse: Almacén + Type: Tipo + Description: Descripción + clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}' + File: Fichero + Select a file: Selecciona un fichero + Allowed content types: Tipos de archivo permitidos + Generate identifier for original file: Generar identificador para archivo original +</i18n> diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js index 1c722afe8..a3b2d52dc 100644 --- a/src/router/modules/worker.js +++ b/src/router/modules/worker.js @@ -12,7 +12,7 @@ export default { redirect: { name: 'WorkerMain' }, menus: { main: ['WorkerList', 'WorkerDepartment'], - card: ['WorkerNotificationsManager', 'WorkerPda'], + card: ['WorkerNotificationsManager', 'WorkerPda', 'WorkerDms'], departmentCard: ['BasicData'], }, children: [ @@ -85,6 +85,35 @@ export default { }, component: () => import('src/pages/Worker/Card/WorkerPda.vue'), }, + { + path: 'dms', + name: 'DmsCard', + redirect: { name: 'WorkerDms' }, + children: [ + { + path: '', + name: 'WorkerDms', + meta: { + title: 'dms', + icon: 'cloud_upload', + }, + component: () => + import('src/pages/Worker/Card/WorkerDms.vue'), + }, + { + path: 'create', + name: 'WorkerDmsCreate', + component: () => + import('src/pages/Worker/components/WorkerDmsCreate.vue'), + }, + { + path: ':dmsId/edit', + name: 'WorkerDmsEdit', + component: () => + import('src/pages/Worker/components/WorkerDmsEdit.vue'), + }, + ], + }, ], }, ], From 627d3bbe85a38232de80a5d92bb67aee98a38291 Mon Sep 17 00:00:00 2001 From: carlosfonseca <carlos.fonseca@mindshore.io> Date: Mon, 25 Mar 2024 16:00:47 -0500 Subject: [PATCH 03/15] Se implementan las correcciones a los comentarios --- src/pages/Worker/Card/WorkerDms.vue | 36 ++- .../Worker/components/WorkerDmsActions.vue | 16 +- .../Worker/components/WorkerDmsCreate.vue | 277 ++---------------- src/pages/Worker/components/WorkerDmsEdit.vue | 262 +++-------------- src/router/modules/worker.js | 32 +- 5 files changed, 103 insertions(+), 520 deletions(-) diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue index 144e3edee..f794f6b17 100644 --- a/src/pages/Worker/Card/WorkerDms.vue +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -1,22 +1,27 @@ <script setup> import { computed, ref, watch } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRoute, useRouter } from 'vue-router'; +import { useRoute } from 'vue-router'; + +import { useQuasar } from 'quasar'; import { downloadFile } from 'src/composables/downloadFile'; import { toDateHour } from 'src/filters'; import FetchData from 'components/FetchData.vue'; +import WorkerDmsCreate from '../components/WorkerDmsCreate.vue'; import WorkerDmsActions from '../components/WorkerDmsActions.vue'; const { t } = useI18n(); +const quasar = useQuasar(); const route = useRoute(); -const router = useRouter(); -const workerDmsRef = ref(null); +const findOne = ref([]); const rows = ref([]); +const workerDmsRef = ref(null); const filter = { where: { worker: route.params.id }, order: ['dmsFk DESC'], limit: 20 }; +const filterFindOne = { where: { code: 'hhrrData' } }; const columns = computed(() => [ { @@ -82,8 +87,15 @@ const setData = () => { workerDmsRef.value.fetch(); }; -const toWorkerDmsCreate = () => { - router.push({ name: 'WorkerDmsCreate' }); +const showWorkerDmsCreate = () => { + quasar.dialog({ + component: WorkerDmsCreate, + componentProps: { + id: Number(route.params.id), + defaultDmsCode: findOne.value.code, + promise: setData, + }, + }); }; </script> @@ -95,6 +107,12 @@ const toWorkerDmsCreate = () => { auto-load :url="`WorkerDms/${route.params.id}/filter`" /> + <fetch-data + :filter="filterFindOne" + @on-fetch="(data) => (findOne = data)" + auto-load + url="DmsTypes/findOne" + /> <QPage class="column items-center q-pa-md"> <QTable @@ -107,7 +125,9 @@ const toWorkerDmsCreate = () => { > <template #body-cell-order="{ row }"> <QTd> - <QChip class="chip-color" square>{{ row.hardCopyNumber }}</QChip> + <QChip class="chip-color" square v-if="row.hardCopyNumber">{{ + row.hardCopyNumber + }}</QChip> </QTd> </template> @@ -138,7 +158,7 @@ const toWorkerDmsCreate = () => { </QPage> <QPageSticky :offset="[18, 18]"> - <QBtn @click.stop="toWorkerDmsCreate()" color="primary" fab icon="add" /> + <QBtn @click.stop="showWorkerDmsCreate" color="primary" fab icon="add" /> <QTooltip> {{ t('Upload file') }} </QTooltip> @@ -147,7 +167,7 @@ const toWorkerDmsCreate = () => { <style scoped> .chip-color { - background-color: var(--vn-label); + background-color: var(--vn-label-color); } </style> diff --git a/src/pages/Worker/components/WorkerDmsActions.vue b/src/pages/Worker/components/WorkerDmsActions.vue index 9a8e4816e..2a6ab1ce3 100644 --- a/src/pages/Worker/components/WorkerDmsActions.vue +++ b/src/pages/Worker/components/WorkerDmsActions.vue @@ -1,17 +1,15 @@ <script setup> import { useI18n } from 'vue-i18n'; -import { useRoute, useRouter } from 'vue-router'; import { useQuasar } from 'quasar'; import { downloadFile } from 'src/composables/downloadFile'; import WorkerDmsDelete from 'src/pages/Worker/components/WorkerDmsDelete.vue'; +import WorkerDmsEdit from './WorkerDmsEdit.vue'; const { t } = useI18n(); const quasar = useQuasar(); -const route = useRoute(); -const router = useRouter(); const $props = defineProps({ id: { @@ -26,12 +24,12 @@ const $props = defineProps({ const setDownloadFile = () => downloadFile($props.id); -const toWorkerDmsEdit = () => { - router.push({ - name: 'WorkerDmsEdit', - params: { - id: route.params.id, +const showWorkerDmsEdit = () => { + quasar.dialog({ + component: WorkerDmsEdit, + componentProps: { dmsId: $props.id, + promise: setData, }, }); }; @@ -64,7 +62,7 @@ const setData = () => { </QTooltip> </QIcon> <QIcon - @click.stop="toWorkerDmsEdit" + @click.stop="showWorkerDmsEdit" class="q-ml-md" color="primary" name="edit" diff --git a/src/pages/Worker/components/WorkerDmsCreate.vue b/src/pages/Worker/components/WorkerDmsCreate.vue index d38e044ca..f9afdddeb 100644 --- a/src/pages/Worker/components/WorkerDmsCreate.vue +++ b/src/pages/Worker/components/WorkerDmsCreate.vue @@ -1,262 +1,37 @@ <script setup> -import { onBeforeMount, ref, watch } from 'vue'; -import { useI18n } from 'vue-i18n'; -import { useRoute, useRouter } from 'vue-router'; +import { useDialogPluginComponent } from 'quasar'; -import axios from 'axios'; +import VnDms from 'src/components/common/VnDms.vue'; -import { useState } from 'src/composables/useState'; -import { useValidator } from 'src/composables/useValidator'; -import useNotify from 'src/composables/useNotify'; +const { dialogRef } = useDialogPluginComponent(); -import FetchData from 'components/FetchData.vue'; -import VnRow from 'components/ui/VnRow.vue'; -import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; -import VnInput from 'src/components/common/VnInput.vue'; - -const { notify } = useNotify(); -const { t } = useI18n(); -const { validate } = useValidator(); -const route = useRoute(); -const router = useRouter(); -const state = useState(); -const user = state.getUser(); - -const filterFindOne = { where: { code: 'paymentsLaw' } }; -const filterCompanies = { order: ['code'] }; -const filterWarehouses = { order: ['name'] }; - -const inputFileRef = ref(); -const client = ref({}); -const findOne = ref([]); -const allowedContentTypes = ref([]); -const optionsCompanies = ref([]); -const optionsWarehouses = ref([]); -const optionsDmsTypes = ref([]); -const isLoading = ref(false); -const dms = ref({ - hasFile: false, +const $props = defineProps({ + id: { + type: Number, + required: true, + }, + defaultDmsCode: { + type: String, + required: true, + }, + promise: { + type: Function, + required: true, + }, }); -onBeforeMount(() => { - const { companyFk, warehouseFk } = user.value; - dms.value.reference = route.params.id; - dms.value.companyId = companyFk; - dms.value.warehouseId = warehouseFk; -}); - -watch([client, findOne], ([newClient, newFindOne]) => { - dms.value.description = t('clientFileDescription', { - dmsTypeName: newFindOne.name?.toUpperCase(), - clientName: newClient.name?.toUpperCase(), - clientId: newClient.id, - }); - dms.value.dmsTypeId = newFindOne.id; -}); - -const saveData = async () => { - try { - const formData = new FormData(); - const files = dms.value.files; - - if (files && files.length > 0) { - for (let file of files) { - formData.append(file.name, file); - } - dms.value.hasFileAttached = true; - - const url = `Workers/${route.params.id}/uploadFile`; - await axios.post(url, formData, { - params: dms.value, - }); - notify('globals.dataSaved', 'positive'); - toWorkerDms(); - } - } catch (error) { - notify(error.message, 'negative'); - } -}; - -const toWorkerDms = () => { - router.push({ name: 'WorkerDms' }); +const onDataSaved = async () => { + if ($props.promise) await $props.promise(); }; </script> <template> - <fetch-data - @on-fetch="(data) => (client = data)" - auto-load - :url="`Clients/${route.params.id}/getCard`" - /> - <fetch-data - :filter="filterFindOne" - @on-fetch="(data) => (findOne = data)" - auto-load - url="DmsTypes/findOne" - /> - <fetch-data - @on-fetch="(data) => (allowedContentTypes = data)" - auto-load - url="DmsContainers/allowedContentTypes" - /> - <fetch-data - :filter="filterCompanies" - @on-fetch="(data) => (optionsCompanies = data)" - auto-load - url="Companies" - /> - <fetch-data - :filter="filterWarehouses" - @on-fetch="(data) => (optionsWarehouses = data)" - auto-load - url="Warehouses" - /> - <fetch-data - :filter="filterWarehouses" - @on-fetch="(data) => (optionsDmsTypes = data)" - auto-load - url="DmsTypes" - /> - - <Teleport to="#st-actions"> - <QBtnGroup push class="q-gutter-x-sm"> - <QBtn - :disabled="isLoading" - :label="t('globals.cancel')" - :loading="isLoading" - @click="toWorkerDms" - color="primary" - flat - icon="close" - /> - <QBtn - :disabled="isLoading" - :label="t('globals.save')" - :loading="isLoading" - @click.stop="saveData" - color="primary" - icon="save" - /> - </QBtnGroup> - </Teleport> - - <div class="full-width flex justify-center"> - <QCard class="card-width q-pa-lg"> - <QCardSection> - <QForm> - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <VnInput - :label="t('Reference')" - clearable - v-model="dms.reference" - /> - </div> - <div class="col"> - <VnSelectFilter - :label="t('Company')" - :options="optionsCompanies" - :rules="validate('entry.companyFk')" - option-label="code" - option-value="id" - v-model="dms.companyId" - /> - </div> - </VnRow> - - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <VnSelectFilter - :label="t('Warehouse')" - :options="optionsWarehouses" - option-label="name" - option-value="id" - v-model="dms.warehouseId" - /> - </div> - <div class="col"> - <VnSelectFilter - :label="t('Type')" - :options="optionsDmsTypes" - option-label="name" - option-value="id" - v-model="dms.dmsTypeId" - /> - </div> - </VnRow> - - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <VnInput - :label="t('Description')" - :rules="validate('route.description')" - clearable - type="textarea" - v-model="dms.description" - /> - </div> - </VnRow> - - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <QFile - ref="inputFileRef" - class="required" - :label="t('File')" - v-model="dms.files" - multiple - :accept="allowedContentTypes.join(',')" - clearable - clear-icon="close" - > - <template #append> - <QBtn - icon="vn:attach" - flat - round - padding="xs" - @click="inputFileRef.pickFiles()" - > - <QTooltip> - {{ t('Select a file') }} - </QTooltip> - </QBtn> - <QBtn icon="info" flat round padding="xs"> - <QTooltip max-width="30rem"> - {{ - `${t( - 'Allowed content types' - )}: ${allowedContentTypes.join(', ')}` - }} - </QTooltip> - </QBtn> - </template> - </QFile> - </div> - </VnRow> - - <QCheckbox - :label="t('Generate identifier for original file')" - v-model="dms.hasFile" - /> - </QForm> - </QCardSection> - </QCard> - </div> + <QDialog ref="dialogRef"> + <VnDms + :default-dms-code="$props.defaultDmsCode" + :url="`Workers/${$props.id}/uploadFile`" + @on-data-saved="onDataSaved" + model="WorkerDmsCreate" + /> + </QDialog> </template> - -<i18n> -en: - clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}' -es: - Reference: Referencia - Company: Empresa - Warehouse: Almacén - Type: Tipo - Description: Descripción - clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}' - File: Fichero - Select a file: Selecciona un fichero - Allowed content types: Tipos de archivo permitidos - Generate identifier for original file: Generar identificador para archivo original -</i18n> diff --git a/src/pages/Worker/components/WorkerDmsEdit.vue b/src/pages/Worker/components/WorkerDmsEdit.vue index 8e84c6326..7656fe09f 100644 --- a/src/pages/Worker/components/WorkerDmsEdit.vue +++ b/src/pages/Worker/components/WorkerDmsEdit.vue @@ -1,246 +1,56 @@ <script setup> import { ref } from 'vue'; -import { useI18n } from 'vue-i18n'; -import { useRoute, useRouter } from 'vue-router'; -import axios from 'axios'; - -import { useValidator } from 'src/composables/useValidator'; -import useNotify from 'src/composables/useNotify'; +import { useDialogPluginComponent } from 'quasar'; import FetchData from 'components/FetchData.vue'; -import VnRow from 'components/ui/VnRow.vue'; -import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; -import VnInput from 'src/components/common/VnInput.vue'; +import VnDms from 'src/components/common/VnDms.vue'; -const { notify } = useNotify(); -const { t } = useI18n(); -const { validate } = useValidator(); -const route = useRoute(); -const router = useRouter(); +const { dialogRef } = useDialogPluginComponent(); -const filterCompanies = { order: ['code'] }; -const filterWarehouses = { order: ['name'] }; +const $props = defineProps({ + dmsId: { + type: Number, + required: true, + }, + defaultDmsCode: { + type: String, + required: false, + }, + promise: { + type: Function, + required: true, + }, +}); -const inputFileRef = ref(); -const allowedContentTypes = ref([]); -const optionsCompanies = ref([]); -const optionsWarehouses = ref([]); -const optionsDmsTypes = ref([]); -const isLoading = ref(false); -const dms = ref({ +const initialData = ref({ hasFile: true, + id: $props.dmsId, }); const setCurrentDms = (data) => { - dms.value.reference = data.reference; - dms.value.companyId = data.companyFk; - dms.value.warehouseId = data.warehouseFk; - dms.value.dmsTypeId = data.dmsTypeFk; - dms.value.description = data.description; - dms.value.hasFile = data.hasFile; + initialData.value.reference = data.reference; + initialData.value.companyFk = data.companyFk; + initialData.value.warehouseFk = data.warehouseFk; + initialData.value.dmsTypeFk = data.dmsTypeFk; + initialData.value.description = data.description; + initialData.value.hasFile = data.hasFile; }; -const saveData = async () => { - try { - const formData = new FormData(); - console.log(formData); - const files = dms.value.files; - console.log(files); - - dms.value.hasFileAttached = files ? true : false; - const url = `dms/${route.params.dmsId}/updateFile`; - - if (files && files.length > 0) { - for (let file of files) { - formData.append(file.name, file); - } - - await axios.post(url, formData, { - params: dms.value, - }); - notify('globals.dataSaved', 'positive'); - } else { - await axios.post(url, dms.value); - } - - toWorkerDms(); - } catch (error) { - notify(error.message, 'negative'); - } -}; - -const toWorkerDms = () => { - router.push({ name: 'WorkerDms' }); +const onDataSaved = async () => { + if ($props.promise) await $props.promise(); }; </script> <template> - <fetch-data :url="`Dms/${route.params.dmsId}`" @on-fetch="setCurrentDms" auto-load /> - <fetch-data - @on-fetch="(data) => (allowedContentTypes = data)" - auto-load - url="DmsContainers/allowedContentTypes" - /> - <fetch-data - :filter="filterCompanies" - @on-fetch="(data) => (optionsCompanies = data)" - auto-load - url="Companies" - /> - <fetch-data - :filter="filterWarehouses" - @on-fetch="(data) => (optionsWarehouses = data)" - auto-load - url="Warehouses" - /> - <fetch-data - :filter="filterWarehouses" - @on-fetch="(data) => (optionsDmsTypes = data)" - auto-load - url="DmsTypes" - /> + <fetch-data :url="`Dms/${$props.dmsId}`" @on-fetch="setCurrentDms" auto-load /> - <Teleport to="#st-actions"> - <QBtnGroup push class="q-gutter-x-sm"> - <QBtn - :disabled="isLoading" - :label="t('globals.cancel')" - :loading="isLoading" - @click="toWorkerDms" - color="primary" - flat - icon="close" - /> - <QBtn - :disabled="isLoading" - :label="t('globals.save')" - :loading="isLoading" - @click.stop="saveData" - color="primary" - icon="save" - /> - </QBtnGroup> - </Teleport> - - <div class="full-width flex justify-center"> - <QCard class="card-width q-pa-lg"> - <QCardSection> - <QForm> - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <VnInput - :label="t('Reference')" - clearable - v-model="dms.reference" - /> - </div> - <div class="col"> - <VnSelectFilter - :label="t('Company')" - :options="optionsCompanies" - :rules="validate('entry.companyFk')" - option-label="code" - option-value="id" - v-model="dms.companyId" - /> - </div> - </VnRow> - - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <VnSelectFilter - :label="t('Warehouse')" - :options="optionsWarehouses" - option-label="name" - option-value="id" - v-model="dms.warehouseId" - /> - </div> - <div class="col"> - <VnSelectFilter - :label="t('Type')" - :options="optionsDmsTypes" - option-label="name" - option-value="id" - v-model="dms.dmsTypeId" - /> - </div> - </VnRow> - - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <VnInput - :label="t('Description')" - :rules="validate('route.description')" - clearable - type="textarea" - v-model="dms.description" - /> - </div> - </VnRow> - - <VnRow class="row q-gutter-md q-mb-md"> - <div class="col"> - <QFile - ref="inputFileRef" - class="required" - :label="t('File')" - v-model="dms.files" - multiple - :accept="allowedContentTypes.join(',')" - clearable - clear-icon="close" - > - <template #append> - <QBtn - icon="vn:attach" - flat - round - padding="xs" - @click="inputFileRef.pickFiles()" - > - <QTooltip> - {{ t('Select a file') }} - </QTooltip> - </QBtn> - <QBtn icon="info" flat round padding="xs"> - <QTooltip max-width="30rem"> - {{ - `${t( - 'Allowed content types' - )}: ${allowedContentTypes.join(', ')}` - }} - </QTooltip> - </QBtn> - </template> - </QFile> - </div> - </VnRow> - - <QCheckbox - :label="t('Generate identifier for original file')" - v-model="dms.hasFile" - disable - /> - </QForm> - </QCardSection> - </QCard> - </div> + <QDialog ref="dialogRef"> + <VnDms + :default-initialData-code="$props.defaultDmsCode" + @on-data-saved="onDataSaved" + model="WorkerDmsEdit" + :form-initial-data="initialData" + /> + </QDialog> </template> - -<i18n> -en: - clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}' -es: - Reference: Referencia - Company: Empresa - Warehouse: Almacén - Type: Tipo - Description: Descripción - clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}' - File: Fichero - Select a file: Selecciona un fichero - Allowed content types: Tipos de archivo permitidos - Generate identifier for original file: Generar identificador para archivo original -</i18n> diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js index a3b2d52dc..c0fe6e1fa 100644 --- a/src/router/modules/worker.js +++ b/src/router/modules/worker.js @@ -86,33 +86,13 @@ export default { component: () => import('src/pages/Worker/Card/WorkerPda.vue'), }, { + name: 'WorkerDms', path: 'dms', - name: 'DmsCard', - redirect: { name: 'WorkerDms' }, - children: [ - { - path: '', - name: 'WorkerDms', - meta: { - title: 'dms', - icon: 'cloud_upload', - }, - component: () => - import('src/pages/Worker/Card/WorkerDms.vue'), - }, - { - path: 'create', - name: 'WorkerDmsCreate', - component: () => - import('src/pages/Worker/components/WorkerDmsCreate.vue'), - }, - { - path: ':dmsId/edit', - name: 'WorkerDmsEdit', - component: () => - import('src/pages/Worker/components/WorkerDmsEdit.vue'), - }, - ], + meta: { + title: 'dms', + icon: 'cloud_upload', + }, + component: () => import('src/pages/Worker/Card/WorkerDms.vue'), }, ], }, From 0d6d18b8c8ddd9046f84a5ad71d54ccba0b7a379 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 27 Mar 2024 20:33:35 -0300 Subject: [PATCH 04/15] Apply VnDmsList component and add docuware action --- src/components/common/VnDmsList.vue | 33 +++- src/composables/downloadFile.js | 9 +- src/composables/getUrl.js | 24 +++ src/pages/Worker/Card/WorkerDms.vue | 185 +----------------- .../Worker/components/WorkerDmsActions.vue | 94 --------- .../Worker/components/WorkerDmsCreate.vue | 37 ---- .../Worker/components/WorkerDmsDelete.vue | 82 -------- src/pages/Worker/components/WorkerDmsEdit.vue | 56 ------ 8 files changed, 67 insertions(+), 453 deletions(-) delete mode 100644 src/pages/Worker/components/WorkerDmsActions.vue delete mode 100644 src/pages/Worker/components/WorkerDmsCreate.vue delete mode 100644 src/pages/Worker/components/WorkerDmsDelete.vue delete mode 100644 src/pages/Worker/components/WorkerDmsEdit.vue diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index 23e00f5d9..9bf90230d 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -9,6 +9,7 @@ import FetchData from 'components/FetchData.vue'; import VnDms from 'src/components/common/VnDms.vue'; import VnConfirm from 'components/ui/VnConfirm.vue'; import { downloadFile } from 'src/composables/downloadFile'; +import { getUrlFindOne } from 'composables/getUrl'; const route = useRoute(); const quasar = useQuasar(); @@ -142,15 +143,17 @@ const columns = computed(() => [ components: [ { component: QBtn, + name: 'download', props: () => ({ icon: 'cloud_download', flat: true, color: 'primary', }), - click: (prop) => downloadFile(prop.row.id), + click: (prop) => downloadFile(prop.row.id, prop.row.isDocuware), }, { component: QBtn, + name: 'edit', props: () => ({ icon: 'edit', flat: true, @@ -160,6 +163,7 @@ const columns = computed(() => [ }, { component: QBtn, + name: 'delete', props: () => ({ icon: 'delete', flat: true, @@ -167,6 +171,16 @@ const columns = computed(() => [ }), click: (prop) => deleteDms(prop.row.id), }, + { + component: QBtn, + name: 'openDocuware', + props: () => ({ + icon: 'open_in_new', + flat: true, + color: 'primary', + }), + click: () => openDocuware(), + }, ], }, ]); @@ -206,6 +220,16 @@ function parseDms(data) { } return data; } + +async function openDocuware() { + const url = await getUrlFindOne('WebClient', 'docuware'); + if (url) window.open(url).focus(); +} + +function shouldRenderButton(buttonName, isDocuware = false) { + // Renderizar el botón si no se llama 'openDocuware' o (se llama 'openDocuware' y props.row.isDocuware = true) + return buttonName !== 'openDocuware' || (buttonName === 'openDocuware' && isDocuware); +} </script> <template> <FetchData @@ -243,6 +267,7 @@ function parseDms(data) { <div class="flex justify-center" v-if="props.col.name == 'options'"> <div v-for="button of props.col.components" :key="button.id"> <component + v-if="shouldRenderButton(button.name, props.row.isDocuware)" :is="button.component" v-bind="button.props(props)" @click="button.click(props)" @@ -272,6 +297,12 @@ function parseDms(data) { class="row" > <component + v-if=" + shouldRenderButton( + button.name, + props.row.isDocuware + ) + " :is="button.component" v-bind="button.props(col)" @click="button.click(col)" diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js index b26dec731..45162f81b 100644 --- a/src/composables/downloadFile.js +++ b/src/composables/downloadFile.js @@ -1,11 +1,12 @@ import { useSession } from 'src/composables/useSession'; import { getUrl } from './getUrl'; -const {getTokenMultimedia} = useSession(); -const token = getTokenMultimedia(); +const { getTokenMultimedia } = useSession(); +const token = getTokenMultimedia(); -export async function downloadFile(dmsId) { +export async function downloadFile(dmsId, isDocuware = false) { let appUrl = await getUrl('', 'lilium'); appUrl = appUrl.replace('/#/', ''); - window.open(`${appUrl}/api/dms/${dmsId}/downloadFile?access_token=${token}`); + const urlPath = isDocuware ? '/docuwareDownload' : '/downloadFile'; + window.open(`${appUrl}/api/dms/${dmsId}${urlPath}?access_token=${token}`); } diff --git a/src/composables/getUrl.js b/src/composables/getUrl.js index 1e6aec4c6..7b7103562 100644 --- a/src/composables/getUrl.js +++ b/src/composables/getUrl.js @@ -1,4 +1,7 @@ import axios from 'axios'; +import useNotify from 'src/composables/useNotify.js'; + +const { notify } = useNotify(); export async function getUrl(route, app = 'salix') { let url; @@ -8,3 +11,24 @@ export async function getUrl(route, app = 'salix') { }); return url; } + +export async function getUrlFindOne(route, app = 'salix') { + try { + const env = process.env.NODE_ENV; + + const filter = { + where: { and: [{ appName: app }, { environment: env }] }, + }; + + const { data } = await axios.get('Urls/findOne', { + params: { filter: JSON.stringify(filter) }, + }); + + if (!data) return null; + + return data.url + route; + } catch (err) { + notify('Direction not found', 'negative'); + console.error('Error finding url: ', err); + } +} diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue index f794f6b17..d0c5cf5e5 100644 --- a/src/pages/Worker/Card/WorkerDms.vue +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -1,184 +1,11 @@ <script setup> -import { computed, ref, watch } from 'vue'; -import { useI18n } from 'vue-i18n'; -import { useRoute } from 'vue-router'; - -import { useQuasar } from 'quasar'; - -import { downloadFile } from 'src/composables/downloadFile'; -import { toDateHour } from 'src/filters'; - -import FetchData from 'components/FetchData.vue'; -import WorkerDmsCreate from '../components/WorkerDmsCreate.vue'; -import WorkerDmsActions from '../components/WorkerDmsActions.vue'; - -const { t } = useI18n(); -const quasar = useQuasar(); -const route = useRoute(); - -const findOne = ref([]); -const rows = ref([]); -const workerDmsRef = ref(null); - -const filter = { where: { worker: route.params.id }, order: ['dmsFk DESC'], limit: 20 }; -const filterFindOne = { where: { code: 'hhrrData' } }; - -const columns = computed(() => [ - { - align: 'left', - field: 'dmsFk', - label: t('Id'), - name: 'id', - }, - { - align: 'left', - field: 'hardCopyNumber', - label: t('Order'), - name: 'order', - }, - { - align: 'left', - field: 'reference', - label: t('Reference'), - name: 'reference', - }, - { - align: 'left', - field: 'description', - label: t('Description'), - name: 'description', - }, - { - align: 'left', - field: 'hasFile', - label: t('Original'), - name: 'original', - }, - { - align: 'left', - field: 'file', - label: t('File'), - name: 'file', - }, - { - align: 'left', - field: 'created', - label: t('Created'), - name: 'created', - format: (value) => toDateHour(value), - }, - { - align: 'right', - field: 'actions', - label: '', - name: 'actions', - }, -]); - -watch( - () => route.params.id, - (newValue) => { - filter.where.worker = newValue; - setData(); - } -); - -const setData = () => { - workerDmsRef.value.fetch(); -}; - -const showWorkerDmsCreate = () => { - quasar.dialog({ - component: WorkerDmsCreate, - componentProps: { - id: Number(route.params.id), - defaultDmsCode: findOne.value.code, - promise: setData, - }, - }); -}; +import VnDmsList from 'src/components/common/VnDmsList.vue'; </script> - <template> - <FetchData - ref="workerDmsRef" - :filter="filter" - @on-fetch="(data) => (rows = data)" - auto-load - :url="`WorkerDms/${route.params.id}/filter`" + <VnDmsList + model="WorkerDms" + update-model="Workers" + default-dms-code="hhrrData" + filter="workerFk" /> - <fetch-data - :filter="filterFindOne" - @on-fetch="(data) => (findOne = data)" - auto-load - url="DmsTypes/findOne" - /> - - <QPage class="column items-center q-pa-md"> - <QTable - :columns="columns" - :pagination="{ rowsPerPage: 12 }" - :rows="rows" - class="full-width q-mt-md" - row-key="id" - v-if="rows?.length" - > - <template #body-cell-order="{ row }"> - <QTd> - <QChip class="chip-color" square v-if="row.hardCopyNumber">{{ - row.hardCopyNumber - }}</QChip> - </QTd> - </template> - - <template #body-cell-original="{ row }"> - <QTd> - <QCheckbox :model-value="row.hasFile === 1" disable /> - </QTd> - </template> - - <template #body-cell-file="{ row }"> - <QTd> - <QBtn @click.stop="downloadFile(row.dmsFk)" color="blue" flat no-caps> - {{ row.file }} - </QBtn> - </QTd> - </template> - - <template #body-cell-actions="{ row }"> - <QTd> - <WorkerDmsActions :id="row.dmsFk" :promise="setData" /> - </QTd> - </template> - </QTable> - - <h5 class="flex justify-center color-vn-label" v-else> - {{ t('globals.noResults') }} - </h5> - </QPage> - - <QPageSticky :offset="[18, 18]"> - <QBtn @click.stop="showWorkerDmsCreate" color="primary" fab icon="add" /> - <QTooltip> - {{ t('Upload file') }} - </QTooltip> - </QPageSticky> </template> - -<style scoped> -.chip-color { - background-color: var(--vn-label-color); -} -</style> - -<i18n> -es: - Id: Id - Order: Orden - Reference: Referencia - Description: Descripción - File: Archivo - Original: Original - Created: Fecha creación - Upload file: Subir fichero -</i18n> diff --git a/src/pages/Worker/components/WorkerDmsActions.vue b/src/pages/Worker/components/WorkerDmsActions.vue deleted file mode 100644 index 2a6ab1ce3..000000000 --- a/src/pages/Worker/components/WorkerDmsActions.vue +++ /dev/null @@ -1,94 +0,0 @@ -<script setup> -import { useI18n } from 'vue-i18n'; - -import { useQuasar } from 'quasar'; - -import { downloadFile } from 'src/composables/downloadFile'; - -import WorkerDmsDelete from 'src/pages/Worker/components/WorkerDmsDelete.vue'; -import WorkerDmsEdit from './WorkerDmsEdit.vue'; - -const { t } = useI18n(); -const quasar = useQuasar(); - -const $props = defineProps({ - id: { - type: Number, - required: true, - }, - promise: { - type: Function, - required: true, - }, -}); - -const setDownloadFile = () => downloadFile($props.id); - -const showWorkerDmsEdit = () => { - quasar.dialog({ - component: WorkerDmsEdit, - componentProps: { - dmsId: $props.id, - promise: setData, - }, - }); -}; - -const showWorkerDmsDeleteDialog = () => { - quasar.dialog({ - component: WorkerDmsDelete, - componentProps: { - id: $props.id, - promise: setData, - }, - }); -}; - -const setData = () => { - $props.promise(); -}; -</script> - -<template> - <div> - <QIcon - @click.stop="setDownloadFile" - color="primary" - name="cloud_download" - size="sm" - > - <QTooltip> - {{ t('actionFile', { action: t('globals.download') }) }} - </QTooltip> - </QIcon> - <QIcon - @click.stop="showWorkerDmsEdit" - class="q-ml-md" - color="primary" - name="edit" - size="sm" - > - <QTooltip> - {{ t('actionFile', { action: t('globals.edit') }) }} - </QTooltip> - </QIcon> - <QIcon - @click.stop="showWorkerDmsDeleteDialog" - class="q-ml-md" - color="primary" - name="delete" - size="sm" - > - <QTooltip> - {{ t('actionFile', { action: t('globals.remove') }) }} - </QTooltip> - </QIcon> - </div> -</template> - -<i18n> -en: - actionFile: '{action} file' -es: - actionFile: '{action} fichero' -</i18n> diff --git a/src/pages/Worker/components/WorkerDmsCreate.vue b/src/pages/Worker/components/WorkerDmsCreate.vue deleted file mode 100644 index f9afdddeb..000000000 --- a/src/pages/Worker/components/WorkerDmsCreate.vue +++ /dev/null @@ -1,37 +0,0 @@ -<script setup> -import { useDialogPluginComponent } from 'quasar'; - -import VnDms from 'src/components/common/VnDms.vue'; - -const { dialogRef } = useDialogPluginComponent(); - -const $props = defineProps({ - id: { - type: Number, - required: true, - }, - defaultDmsCode: { - type: String, - required: true, - }, - promise: { - type: Function, - required: true, - }, -}); - -const onDataSaved = async () => { - if ($props.promise) await $props.promise(); -}; -</script> - -<template> - <QDialog ref="dialogRef"> - <VnDms - :default-dms-code="$props.defaultDmsCode" - :url="`Workers/${$props.id}/uploadFile`" - @on-data-saved="onDataSaved" - model="WorkerDmsCreate" - /> - </QDialog> -</template> diff --git a/src/pages/Worker/components/WorkerDmsDelete.vue b/src/pages/Worker/components/WorkerDmsDelete.vue deleted file mode 100644 index 5e1b98b24..000000000 --- a/src/pages/Worker/components/WorkerDmsDelete.vue +++ /dev/null @@ -1,82 +0,0 @@ -<script setup> -import { ref } from 'vue'; -import { useI18n } from 'vue-i18n'; - -import { useDialogPluginComponent } from 'quasar'; -import axios from 'axios'; - -import useNotify from 'src/composables/useNotify'; - -const $props = defineProps({ - id: { - type: Number, - required: true, - }, - promise: { - type: Function, - required: true, - }, -}); - -const { dialogRef } = useDialogPluginComponent(); -const { notify } = useNotify(); -const { t } = useI18n(); - -const closeButton = ref(null); - -const isLoading = ref(false); - -const deleteDms = async () => { - isLoading.value = true; - try { - await axios.post(`WorkerDms/${$props.id}/removeFile`); - if ($props.promise) await $props.promise(); - notify('globals.dataDeleted', 'positive'); - } catch (error) { - notify(error.message, 'negative'); - } finally { - closeButton.value.click(); - isLoading.value = false; - } -}; -</script> - -<template> - <QDialog ref="dialogRef"> - <QCard class="q-pa-md q-mb-md"> - <span ref="closeButton" class="row justify-end close-icon" v-close-popup> - <QIcon name="close" size="sm" /> - </span> - - <QCardSection> - <div class="mt-1 text-h6">{{ t('This file will be deleted') }}</div> - <div>{{ t('Are you sure you want to continue?') }}</div> - </QCardSection> - - <QCardActions class="flex justify-end"> - <QBtn - :disabled="isLoading" - :label="t('globals.cancel')" - :loading="isLoading" - class="q-mr-xl" - color="primary" - flat - v-close-popup - /> - <QBtn - :disabled="isLoading" - :label="t('globals.save')" - :loading="isLoading" - @click.stop="deleteDms" - color="primary" - /> - </QCardActions> - </QCard> - </QDialog> -</template> - -<i18n> -es: - This file will be deleted: Este fichero va a ser borrado - Are you sure you want to continue?: ¿Seguro que quieres continuar? -</i18n> diff --git a/src/pages/Worker/components/WorkerDmsEdit.vue b/src/pages/Worker/components/WorkerDmsEdit.vue deleted file mode 100644 index 7656fe09f..000000000 --- a/src/pages/Worker/components/WorkerDmsEdit.vue +++ /dev/null @@ -1,56 +0,0 @@ -<script setup> -import { ref } from 'vue'; - -import { useDialogPluginComponent } from 'quasar'; - -import FetchData from 'components/FetchData.vue'; -import VnDms from 'src/components/common/VnDms.vue'; - -const { dialogRef } = useDialogPluginComponent(); - -const $props = defineProps({ - dmsId: { - type: Number, - required: true, - }, - defaultDmsCode: { - type: String, - required: false, - }, - promise: { - type: Function, - required: true, - }, -}); - -const initialData = ref({ - hasFile: true, - id: $props.dmsId, -}); - -const setCurrentDms = (data) => { - initialData.value.reference = data.reference; - initialData.value.companyFk = data.companyFk; - initialData.value.warehouseFk = data.warehouseFk; - initialData.value.dmsTypeFk = data.dmsTypeFk; - initialData.value.description = data.description; - initialData.value.hasFile = data.hasFile; -}; - -const onDataSaved = async () => { - if ($props.promise) await $props.promise(); -}; -</script> - -<template> - <fetch-data :url="`Dms/${$props.dmsId}`" @on-fetch="setCurrentDms" auto-load /> - - <QDialog ref="dialogRef"> - <VnDms - :default-initialData-code="$props.defaultDmsCode" - @on-data-saved="onDataSaved" - model="WorkerDmsEdit" - :form-initial-data="initialData" - /> - </QDialog> -</template> From f12ec3c0c9a6cf7e245fda5113fb2b0c92904f36 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Mon, 8 Apr 2024 08:47:29 -0300 Subject: [PATCH 05/15] Add redirection to summary and open summary action --- src/i18n/en/index.js | 1 + src/i18n/es/index.js | 1 + src/pages/Item/ItemList.vue | 17 ++++++++++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 6c4cc34f3..0a17ee45f 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -1167,6 +1167,7 @@ export default { list: { id: 'Identifier', grouping: 'Grouping', + packing: 'Packing', description: 'Description', stems: 'Stems', category: 'Category', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 4ad9a1d5c..f484c6cc6 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -1167,6 +1167,7 @@ export default { list: { id: 'Identificador', grouping: 'Grouping', + packing: 'Packing', description: 'Descripción', stems: 'Tallos', category: 'Reino', diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 14549296b..9baf3061a 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -10,18 +10,21 @@ import VnInput from 'src/components/common/VnInput.vue'; import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import ItemSummary from '../Item/Card/ItemSummary.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDate, toCurrency } from 'src/filters'; import { useSession } from 'composables/useSession'; import { dashIfEmpty } from 'src/filters'; import { useArrayData } from 'composables/useArrayData'; +import { useSummaryDialog } from 'src/composables/useSummaryDialog'; const router = useRouter(); const session = useSession(); const token = session.getToken(); const stateStore = useStateStore(); const { t } = useI18n(); +const { viewSummary } = useSummaryDialog(); const itemTypesOptions = ref([]); const originsOptions = ref([]); @@ -373,8 +376,8 @@ const redirectToItemCreate = () => { // router.push({ name: 'EntryBuys', params: { id: entryFk } }); }; -const redirectToItemSummary = () => { - // router.push({ name: 'EntryBuys', params: { id: entryFk } }); +const redirectToItemSummary = (id) => { + router.push({ name: 'ItemSummary', params: { id } }); }; // const applyColumnFilter = async (col) => { @@ -386,6 +389,10 @@ const redirectToItemSummary = () => { // } // }; +const cloneRow = () => { + console.log('cloneRow'); +}; + onMounted(async () => { stateStore.rightDrawer = true; const filteredColumns = columns.value.filter((col) => col.name !== 'picture'); @@ -507,10 +514,10 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <QCheckbox :model-value="!!row.isActive" disable /> </QTd> </template> - <template #body-cell-actions="{}"> + <template #body-cell-actions="{ row }"> <QTd> <QIcon - @click.stop="viewSummary($props.id, CustomerSummary)" + @click.stop="cloneRow()" class="q-ml-sm" color="primary" name="vn:clone" @@ -521,7 +528,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </QTooltip> </QIcon> <QIcon - @click.stop="viewSummary($props.id, CustomerSummary)" + @click.stop="viewSummary(row.id, ItemSummary)" class="q-ml-md" color="primary" name="preview" From 64ff5e44460be30d3566d8779aa6b76f0fc14f07 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Mon, 8 Apr 2024 11:40:44 -0300 Subject: [PATCH 06/15] Add pagination and table actions --- src/pages/Item/ItemList.vue | 212 ++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 93 deletions(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 9baf3061a..773cb0771 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -11,6 +11,7 @@ import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import ItemSummary from '../Item/Card/ItemSummary.vue'; +import VnPaginate from 'components/ui/VnPaginate.vue'; import { useStateStore } from 'stores/useStateStore'; import { toDate, toCurrency } from 'src/filters'; @@ -18,13 +19,16 @@ import { useSession } from 'composables/useSession'; import { dashIfEmpty } from 'src/filters'; import { useArrayData } from 'composables/useArrayData'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; +import { useVnConfirm } from 'composables/useVnConfirm'; +import axios from 'axios'; const router = useRouter(); -const session = useSession(); -const token = session.getToken(); +const { getTokenMultimedia } = useSession(); +const token = getTokenMultimedia(); const stateStore = useStateStore(); const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); +const { openConfirmationModal } = useVnConfirm(); const itemTypesOptions = ref([]); const originsOptions = ref([]); @@ -66,13 +70,6 @@ const exprBuilder = (param, value) => { }; const params = reactive({}); -const arrayData = useArrayData('ItemList', { - url: 'Items/filter', - order: ['isActive DESC', 'name', 'id'], - exprBuilder: exprBuilder, -}); -const store = arrayData.store; -const rows = computed(() => store.data); // const getInputEvents = (col) => { // return col.columnFilter.type === 'select' @@ -389,15 +386,25 @@ const redirectToItemSummary = (id) => { // } // }; -const cloneRow = () => { - console.log('cloneRow'); +const cloneItem = async (itemFk) => { + try { + console.log('cloneRow: ', itemFk); + const { data } = await axios.post(`Items/${itemFk}/clone`); + if (!data) return; + router.push({ name: 'ItemTags', params: { id: data.id } }); + } catch (err) { + console.error('Error cloning row', err); + } +}; + +const test = (ev) => { + console.log('test: ', ev); }; onMounted(async () => { stateStore.rightDrawer = true; const filteredColumns = columns.value.filter((col) => col.name !== 'picture'); allColumnNames.value = filteredColumns.map((col) => col.name); - await arrayData.fetch({ append: false }); }); onUnmounted(() => (stateStore.rightDrawer = false)); @@ -440,23 +447,29 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <QSpace /> <div id="st-actions"></div> </QToolbar> - <!-- <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> - <QScrollArea class="fit text-grey-8"> - <EntryLatestBuysFilter data-key="EntryLatestBuys" :tags="tags" /> - </QScrollArea> - </QDrawer> --> <QPage class="column items-center q-pa-md"> - <QTable - :rows="rows" - :columns="columns" - row-key="id" - :pagination="{ rowsPerPage: 0 }" - class="full-width q-mt-md" - :visible-columns="visibleColumns" - :no-data-label="t('globals.noResults')" - @row-click="(_, row) => redirectToItemSummary(row.id)" + <VnPaginate + data-key="ItemList" + url="Items/filter" + :order="['isActive DESC', 'name', 'id']" + :limit="12" + :offset="50" + auto-load > - <!-- <template #top-row="{ cols }"> + <template #body="{ rows }"> + <QTable + :rows="rows" + :columns="columns" + row-key="id" + :pagination="{ rowsPerPage: 0 }" + class="full-width q-mt-md" + :visible-columns="visibleColumns" + :no-data-label="t('globals.noResults')" + virtual-scroll + @virtual-scroll="test" + @row-click="(_, row) => redirectToItemSummary(row.id)" + > + <!-- <template #top-row="{ cols }"> <QTr> <QTd /> <QTd @@ -475,75 +488,84 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </QTd> </QTr> </template> --> - <template #body-cell-picture="{ row }"> - <QTd> - <QImg - :src="`/api/Images/catalog/50x50/${row.id}/download?access_token=${token}`" - spinner-color="primary" - :ratio="1" - height="50px" - width="50px" - class="image" - /> - </QTd> + <template #body-cell-picture="{ row }"> + <QTd> + <QImg + :src="`/api/Images/catalog/50x50/${row.id}/download?access_token=${token}`" + spinner-color="primary" + :ratio="1" + height="50px" + width="50px" + class="image" + /> + </QTd> + </template> + <template #body-cell-id="{ row }"> + <QTd @click.stop> + <QBtn flat color="primary"> + {{ row.id }} + </QBtn> + <ItemDescriptorProxy :id="row.id" /> + </QTd> + </template> + <template #body-cell-userName="{ row }"> + <QTd @click.stop> + <QBtn flat color="primary" dense> + {{ row.userName }} + </QBtn> + <WorkerDescriptorProxy :id="row.buyerFk" /> + </QTd> + </template> + <template #body-cell-description="{ row }"> + <QTd class="col"> + <span>{{ row.name }} {{ row.subName }}</span> + <fetched-tags :item="row" :max-length="6" /> + </QTd> + </template> + <template #body-cell-isActive="{ row }"> + <QTd> + <QCheckbox :model-value="!!row.isActive" disable /> + </QTd> + </template> + <template #body-cell-actions="{ row }"> + <QTd> + <QIcon + @click.stop=" + openConfirmationModal( + t(`All it's properties will be copied`), + t('Do you want to clone this item?'), + () => cloneItem(row.id) + ) + " + class="q-ml-sm" + color="primary" + name="vn:clone" + size="sm" + > + <QTooltip> + {{ t('Clone') }} + </QTooltip> + </QIcon> + <QIcon + @click.stop="viewSummary(row.id, ItemSummary)" + class="q-ml-md" + color="primary" + name="preview" + size="sm" + > + <QTooltip class="text-no-wrap"> + {{ t('Preview') }} + </QTooltip> + </QIcon> + </QTd> + </template> + </QTable> </template> - <template #body-cell-id="{ row }"> - <QTd @click.stop> - <QBtn flat color="primary"> - {{ row.id }} - </QBtn> - <ItemDescriptorProxy :id="row.id" /> - </QTd> - </template> - <template #body-cell-userName="{ row }"> - <QTd @click.stop> - <QBtn flat color="primary" dense> - {{ row.userName }} - </QBtn> - <WorkerDescriptorProxy :id="row.buyerFk" /> - </QTd> - </template> - <template #body-cell-description="{ row }"> - <QTd class="col"> - <span>{{ row.name }} {{ row.subName }}</span> - <fetched-tags :item="row" :max-length="6" /> - </QTd> - </template> - <template #body-cell-isActive="{ row }"> - <QTd> - <QCheckbox :model-value="!!row.isActive" disable /> - </QTd> - </template> - <template #body-cell-actions="{ row }"> - <QTd> - <QIcon - @click.stop="cloneRow()" - class="q-ml-sm" - color="primary" - name="vn:clone" - size="sm" - > - <QTooltip> - {{ t('Preview') }} - </QTooltip> - </QIcon> - <QIcon - @click.stop="viewSummary(row.id, ItemSummary)" - class="q-ml-md" - color="primary" - name="preview" - size="sm" - > - <QTooltip> - {{ t('Preview') }} - </QTooltip> - </QIcon> - </QTd> - </template> - </QTable> + </VnPaginate> + <QPageSticky :offset="[20, 20]"> <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" /> - <QTooltip> + <QTooltip class="text-no-wrap"> {{ t('New item') }} </QTooltip> </QPageSticky> @@ -553,4 +575,8 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <i18n> es: New item: Nuevo artículo + All it's properties will be copied: Todas sus propiedades serán copiadas + Do you want to clone this item?: ¿Desea clonar este artículo? + Clone: Clonar + Preview: Vista previa </i18n> From e51ea9996b5bb8fb797ec1085313cd24b69bbea3 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Tue, 9 Apr 2024 08:18:18 -0300 Subject: [PATCH 07/15] Item list view --- src/components/ui/VnPaginate.vue | 7 +- src/composables/useArrayData.js | 2 + src/i18n/en/index.js | 2 + src/i18n/es/index.js | 2 + src/pages/Item/ItemCreate.vue | 1 + src/pages/Item/ItemList.vue | 443 +++++++++++++++---------------- src/router/modules/item.js | 8 + 7 files changed, 239 insertions(+), 226 deletions(-) create mode 100644 src/pages/Item/ItemCreate.vue diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue index d1b2f5ccb..fc81c82b1 100644 --- a/src/components/ui/VnPaginate.vue +++ b/src/components/ui/VnPaginate.vue @@ -61,7 +61,6 @@ const props = defineProps({ }); const emit = defineEmits(['onFetch', 'onPaginate']); -defineExpose({ fetch }); const isLoading = ref(false); const pagination = ref({ sortBy: props.order, @@ -91,6 +90,10 @@ watch( } ); +const addFilter = async (filter, params) => { + await arrayData.addFilter({ filter, params }); +}; + async function fetch() { await arrayData.fetch({ append: false }); if (!arrayData.hasMoreData.value) { @@ -140,6 +143,8 @@ async function onLoad(index, done) { if (store.userParamsChanged) isDone = !arrayData.hasMoreData.value; done(isDone); } + +defineExpose({ fetch, addFilter }); </script> <template> diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index b6b81f2d5..74228d8dc 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -145,6 +145,8 @@ export function useArrayData(key, userOptions) { store.userParams = userParams; store.skip = 0; store.filter.skip = 0; + page.value = 1; + await fetch({ append: false }); return { filter, params }; } diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 710b1d5bf..59082422a 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -1208,6 +1208,7 @@ export default { list: 'List', diary: 'Diary', tags: 'Tags', + create: 'Create', }, descriptor: { item: 'Item', @@ -1229,6 +1230,7 @@ export default { category: 'Category', type: 'Type', intrastat: 'Intrastat', + isActive: 'Active', size: 'Size', origin: 'Origin', userName: 'Buyer', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 3a5da882d..16b2e9394 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -1207,6 +1207,7 @@ export default { list: 'Listado', diary: 'Histórico', tags: 'Etiquetas', + create: 'Crear', }, descriptor: { item: 'Artículo', @@ -1228,6 +1229,7 @@ export default { category: 'Reino', type: 'Tipo', intrastat: 'Intrastat', + isActive: 'Activo', size: 'Medida', origin: 'Origen', weightByPiece: 'Peso (gramos)/tallo', diff --git a/src/pages/Item/ItemCreate.vue b/src/pages/Item/ItemCreate.vue new file mode 100644 index 000000000..e20f67ac3 --- /dev/null +++ b/src/pages/Item/ItemCreate.vue @@ -0,0 +1 @@ +<template>Item create view</template> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 773cb0771..304145e23 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -14,10 +14,9 @@ import ItemSummary from '../Item/Card/ItemSummary.vue'; import VnPaginate from 'components/ui/VnPaginate.vue'; import { useStateStore } from 'stores/useStateStore'; -import { toDate, toCurrency } from 'src/filters'; +import { toDateFormat } from 'src/filters/date.js'; import { useSession } from 'composables/useSession'; import { dashIfEmpty } from 'src/filters'; -import { useArrayData } from 'composables/useArrayData'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useVnConfirm } from 'composables/useVnConfirm'; import axios from 'axios'; @@ -30,11 +29,12 @@ const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); const { openConfirmationModal } = useVnConfirm(); +const paginateRef = ref(null); const itemTypesOptions = ref([]); const originsOptions = ref([]); -const itemFamiliesOptions = ref([]); -// const intrastatOptions = ref([]); -const packagingsOptions = ref([]); +const buyersOptions = ref([]); +const intrastatOptions = ref([]); +const itemCategoriesOptions = ref([]); const visibleColumns = ref([]); const allColumnNames = ref([]); @@ -71,19 +71,30 @@ const exprBuilder = (param, value) => { const params = reactive({}); -// const getInputEvents = (col) => { -// return col.columnFilter.type === 'select' -// ? { 'update:modelValue': () => applyColumnFilter(col) } -// : { -// 'keyup.enter': () => applyColumnFilter(col), -// }; -// }; +const applyColumnFilter = async (col) => { + try { + const paramKey = col.columnFilter?.filterParamKey || col.field; + params[paramKey] = col.columnFilter.filterValue; + await paginateRef.value.addFilter(null, params); + } catch (err) { + console.error('Error applying column filter', err); + } +}; + +const getInputEvents = (col) => { + return col.columnFilter.type === 'select' + ? { 'update:modelValue': () => applyColumnFilter(col) } + : { + 'keyup.enter': () => applyColumnFilter(col), + }; +}; const columns = computed(() => [ { label: '', name: 'picture', align: 'left', + columnFilter: null, }, { label: t('item.list.id'), @@ -91,15 +102,15 @@ const columns = computed(() => [ field: 'id', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, }, { label: t('item.list.grouping'), @@ -107,15 +118,15 @@ const columns = computed(() => [ name: 'grouping', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, format: (val) => dashIfEmpty(val), }, { @@ -124,33 +135,32 @@ const columns = computed(() => [ name: 'packing', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, format: (val) => dashIfEmpty(val), }, { label: t('globals.description'), - field: 'description', + field: 'name', name: 'description', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, - // format: (val) => dashIfEmpty(val), + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, }, { label: t('item.list.stems'), @@ -158,15 +168,15 @@ const columns = computed(() => [ name: 'stems', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, }, { label: t('item.list.size'), @@ -174,15 +184,15 @@ const columns = computed(() => [ name: 'size', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, }, { label: t('item.list.type'), @@ -190,18 +200,19 @@ const columns = computed(() => [ name: 'typeName', align: 'left', sortable: true, - // columnFilter: { - // component: VnSelectFilter, - // type: 'select', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // options: itemTypesOptions.value, - // 'option-value': 'code', - // 'option-label': 'code', - // dense: true, - // }, - // }, + columnFilter: { + component: VnSelectFilter, + filterParamKey: 'typeFk', + type: 'select', + filterValue: null, + event: getInputEvents, + attrs: { + options: itemTypesOptions.value, + 'option-value': 'id', + 'option-label': 'name', + dense: true, + }, + }, }, { @@ -210,18 +221,18 @@ const columns = computed(() => [ name: 'category', align: 'left', sortable: true, - // columnFilter: { - // component: VnSelectFilter, - // type: 'select', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // options: intrastatOptions.value, - // 'option-value': 'description', - // 'option-label': 'description', - // dense: true, - // }, - // }, + columnFilter: { + component: VnSelectFilter, + type: 'select', + filterValue: null, + event: getInputEvents, + attrs: { + options: itemCategoriesOptions.value, + 'option-value': 'name', + 'option-label': 'name', + dense: true, + }, + }, }, { @@ -230,18 +241,18 @@ const columns = computed(() => [ name: 'intrastat', align: 'left', sortable: true, - // columnFilter: { - // component: VnSelectFilter, - // type: 'select', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // options: originsOptions.value, - // 'option-value': 'code', - // 'option-label': 'code', - // dense: true, - // }, - // }, + columnFilter: { + component: VnSelectFilter, + type: 'select', + filterValue: null, + event: getInputEvents, + attrs: { + options: intrastatOptions.value, + 'option-value': 'description', + 'option-label': 'description', + dense: true, + }, + }, }, { label: t('item.list.origin'), @@ -249,15 +260,18 @@ const columns = computed(() => [ name: 'origin', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnSelectFilter, + type: 'select', + filterValue: null, + event: getInputEvents, + attrs: { + options: originsOptions.value, + 'option-value': 'code', + 'option-label': 'code', + dense: true, + }, + }, }, { label: t('item.list.userName'), @@ -265,18 +279,19 @@ const columns = computed(() => [ name: 'userName', align: 'left', sortable: true, - // columnFilter: { - // component: VnSelectFilter, - // type: 'select', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // options: itemFamiliesOptions.value, - // 'option-value': 'code', - // 'option-label': 'code', - // dense: true, - // }, - // }, + columnFilter: { + component: VnSelectFilter, + filterParamKey: 'buyerFk', + type: 'select', + filterValue: null, + event: getInputEvents, + attrs: { + options: buyersOptions.value, + 'option-value': 'id', + 'option-label': 'nickname', + dense: true, + }, + }, }, { label: t('item.list.weightByPiece'), @@ -284,15 +299,15 @@ const columns = computed(() => [ name: 'weightByPiece', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, format: (val) => dashIfEmpty(val), }, { @@ -301,32 +316,24 @@ const columns = computed(() => [ name: 'stemMultiplier', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, format: (val) => dashIfEmpty(val), }, { - label: t('entry.latestBuys.isActive'), + label: t('item.list.isActive'), field: 'isActive', name: 'isActive', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: null, }, { label: t('item.list.producer'), @@ -334,15 +341,15 @@ const columns = computed(() => [ name: 'producer', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, format: (val) => dashIfEmpty(val), }, { @@ -351,44 +358,27 @@ const columns = computed(() => [ name: 'landed', align: 'left', sortable: true, - // columnFilter: { - // component: VnInput, - // type: 'text', - // filterValue: null, - // event: getInputEvents, - // attrs: { - // dense: true, - // }, - // }, - format: (val) => dashIfEmpty(toDate(val)), + format: (val) => dashIfEmpty(toDateFormat(val)), + columnFilter: null, }, { label: '', name: 'actions', align: 'left', + columnFilter: null, }, ]); const redirectToItemCreate = () => { - // router.push({ name: 'EntryBuys', params: { id: entryFk } }); + router.push({ name: 'ItemCreate' }); }; const redirectToItemSummary = (id) => { router.push({ name: 'ItemSummary', params: { id } }); }; -// const applyColumnFilter = async (col) => { -// try { -// params[col.field] = col.columnFilter.filterValue; -// await arrayData.addFilter({ params }); -// } catch (err) { -// console.error('Error applying column filter', err); -// } -// }; - const cloneItem = async (itemFk) => { try { - console.log('cloneRow: ', itemFk); const { data } = await axios.post(`Items/${itemFk}/clone`); if (!data) return; router.push({ name: 'ItemTags', params: { id: data.id } }); @@ -397,10 +387,6 @@ const cloneItem = async (itemFk) => { } }; -const test = (ev) => { - console.log('test: ', ev); -}; - onMounted(async () => { stateStore.rightDrawer = true; const filteredColumns = columns.value.filter((col) => col.name !== 'picture'); @@ -411,30 +397,37 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> - <!-- <FetchData + <FetchData url="ItemTypes" - :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }" + :filter="{ fields: ['id', 'name'], order: 'name ASC' }" auto-load @on-fetch="(data) => (itemTypesOptions = data)" - /> --> - <!-- <FetchData + /> + <FetchData + url="ItemCategories" + :filter="{ fields: ['name'], order: 'name ASC' }" + auto-load + @on-fetch="(data) => (itemCategoriesOptions = data)" + /> + <FetchData + url="Intrastats" + :filter="{ fields: ['description'], order: 'description ASC' }" + auto-load + @on-fetch="(data) => (intrastatOptions = data)" + /> + <FetchData url="Origins" - :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }" + :filter="{ fields: ['code'], order: 'code ASC' }" auto-load @on-fetch="(data) => (originsOptions = data)" - /> --> - <!-- <FetchData - url="ItemFamilies" - :filter="{ fields: ['code'], order: 'code ASC', limit: 30 }" + /> + <FetchData + url="TicketRequests/getItemTypeWorker" + :filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }" auto-load - @on-fetch="(data) => (itemFamiliesOptions = data)" - /> --> - <!-- <FetchData - url="Packagings" - :filter="{ fields: ['id'], order: 'id ASC', limit: 30 }" - auto-load - @on-fetch="(data) => (packagingsOptions = data)" - /> --> + @on-fetch="(data) => (buyersOptions = data)" + /> + <QToolbar class="bg-vn-dark justify-end"> <div id="st-data"> <TableVisibleColumns @@ -449,10 +442,13 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </QToolbar> <QPage class="column items-center q-pa-md"> <VnPaginate + ref="paginateRef" data-key="ItemList" url="Items/filter" :order="['isActive DESC', 'name', 'id']" :limit="12" + :expr-builder="exprBuilder" + :user-params="params" :offset="50" auto-load > @@ -465,29 +461,26 @@ onUnmounted(() => (stateStore.rightDrawer = false)); class="full-width q-mt-md" :visible-columns="visibleColumns" :no-data-label="t('globals.noResults')" - virtual-scroll - @virtual-scroll="test" @row-click="(_, row) => redirectToItemSummary(row.id)" > - <!-- <template #top-row="{ cols }"> - <QTr> - <QTd /> - <QTd - v-for="(col, index) in cols" - :key="index" - style="max-width: 100px" - > - <component - :is="col.columnFilter.component" - v-if="col.name !== 'picture'" - v-model="col.columnFilter.filterValue" - v-bind="col.columnFilter.attrs" - v-on="col.columnFilter.event(col)" - dense - /> - </QTd> - </QTr> - </template> --> + <template #top-row="{ cols }"> + <QTr> + <QTd + v-for="(col, index) in cols" + :key="index" + style="max-width: 100px" + > + <component + :is="col.columnFilter.component" + v-if="col.columnFilter" + v-model="col.columnFilter.filterValue" + v-bind="col.columnFilter.attrs" + v-on="col.columnFilter.event(col)" + dense + /> + </QTd> + </QTr> + </template> <template #body-cell-picture="{ row }"> <QTd> <QImg diff --git a/src/router/modules/item.js b/src/router/modules/item.js index 700278617..d16022f79 100644 --- a/src/router/modules/item.js +++ b/src/router/modules/item.js @@ -29,6 +29,14 @@ export default { }, component: () => import('src/pages/Item/ItemList.vue'), }, + { + path: 'create', + name: 'ItemCreate', + meta: { + title: 'create', + }, + component: () => import('src/pages/Item/ItemCreate.vue'), + }, ], }, { From f6e140e369f54bf41c77fb3fd05e584e740eeaa7 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Tue, 9 Apr 2024 08:28:00 -0300 Subject: [PATCH 08/15] Apply workerDms changes --- src/components/common/VnDms.vue | 2 + src/components/common/VnDmsList.vue | 225 +++++++++++------- src/components/ui/VnPaginate.vue | 15 +- src/components/ui/VnTree.vue | 2 +- src/composables/downloadFile.js | 5 +- src/composables/getUrl.js | 24 -- src/composables/useArrayData.js | 2 +- src/i18n/en/index.js | 2 + src/i18n/es/index.js | 2 + .../Department/Card/DepartmentDescriptor.vue | 2 +- src/pages/Worker/Card/WorkerDms.vue | 7 +- 11 files changed, 163 insertions(+), 125 deletions(-) diff --git a/src/components/common/VnDms.vue b/src/components/common/VnDms.vue index 08255f8a5..daef5940b 100644 --- a/src/components/common/VnDms.vue +++ b/src/components/common/VnDms.vue @@ -198,9 +198,11 @@ function addDefaultData(data) { en: contentTypesInfo: Allowed file types {allowedContentTypes} EntryDmsDescription: Reference {reference} + WorkersDescription: Working of employee id {reference} es: Generate identifier for original file: Generar identificador para archivo original contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes} EntryDmsDescription: Referencia {reference} + WorkersDescription: Laboral del empleado {reference} </i18n> diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index 9bf90230d..8c3ffc807 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -5,11 +5,12 @@ import { useRoute } from 'vue-router'; import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar'; import axios from 'axios'; -import FetchData from 'components/FetchData.vue'; +import VnPaginate from 'components/ui/VnPaginate.vue'; import VnDms from 'src/components/common/VnDms.vue'; import VnConfirm from 'components/ui/VnConfirm.vue'; +import VnInputDate from 'components/common/VnInputDate.vue'; +import VnUserLink from '../ui/VnUserLink.vue'; import { downloadFile } from 'src/composables/downloadFile'; -import { getUrlFindOne } from 'composables/getUrl'; const route = useRoute(); const quasar = useQuasar(); @@ -27,6 +28,11 @@ const $props = defineProps({ type: String, default: null, }, + downloadModel: { + type: String, + required: false, + default: null, + }, defaultDmsCode: { type: String, required: true, @@ -75,7 +81,7 @@ const dmsFilter = { ], }, }, - order: ['dmsFk DESC'], + where: { [$props.filter]: route.params.id }, }; const columns = computed(() => [ @@ -95,12 +101,12 @@ const columns = computed(() => [ props: (prop) => ({ readonly: true, borderless: true, - 'model-value': prop.row.dmsType.name, + 'model-value': prop.row.dmsType?.name, }), }, { align: 'left', - field: 'order', + field: 'hardCopyNumber', label: t('globals.order'), name: 'order', component: 'span', @@ -118,6 +124,7 @@ const columns = computed(() => [ label: t('globals.description'), name: 'description', component: 'span', + props: (prop) => ({ value: prop.value?.toUpperCase() }), }, { align: 'left', @@ -137,6 +144,28 @@ const columns = computed(() => [ name: 'file', component: 'span', }, + { + align: 'left', + field: 'worker', + label: t('globals.worker'), + name: 'worker', + component: VnUserLink, + props: (prop) => ({ + name: prop.row.worker?.user?.name.toLowerCase(), + workerId: prop.row.worker?.id, + }), + }, + { + align: 'left', + field: 'created', + label: t('globals.created'), + name: 'created', + component: VnInputDate, + props: (prop) => ({ + disable: true, + 'model-value': prop.row.created, + }), + }, { field: 'options', name: 'options', @@ -144,16 +173,24 @@ const columns = computed(() => [ { component: QBtn, name: 'download', + isDocuware: true, props: () => ({ icon: 'cloud_download', flat: true, color: 'primary', }), - click: (prop) => downloadFile(prop.row.id, prop.row.isDocuware), + click: (prop) => + downloadFile( + prop.row.id, + $props.downloadModel, + null, + prop.row.download + ), }, { component: QBtn, name: 'edit', + external: false, props: () => ({ icon: 'edit', flat: true, @@ -164,6 +201,7 @@ const columns = computed(() => [ { component: QBtn, name: 'delete', + external: false, props: () => ({ icon: 'delete', flat: true, @@ -173,20 +211,22 @@ const columns = computed(() => [ }, { component: QBtn, - name: 'openDocuware', + name: 'open', + external: true, props: () => ({ icon: 'open_in_new', flat: true, color: 'primary', }), - click: () => openDocuware(), + click: (prop) => open(prop.row.url), }, ], }, ]); function setData(data) { - const newData = data.map((value) => value.dms); + const newData = data.map((value) => value.dms || value); + newData.sort((a, b) => new Date(b.created) - new Date(a.created)); rows.value = newData; } @@ -221,100 +261,105 @@ function parseDms(data) { return data; } -async function openDocuware() { - const url = await getUrlFindOne('WebClient', 'docuware'); - if (url) window.open(url).focus(); +async function open(url) { + window.open(url).focus(); } -function shouldRenderButton(buttonName, isDocuware = false) { - // Renderizar el botón si no se llama 'openDocuware' o (se llama 'openDocuware' y props.row.isDocuware = true) - return buttonName !== 'openDocuware' || (buttonName === 'openDocuware' && isDocuware); +function shouldRenderButton(button, isExternal = false) { + if (button.name == 'download') return true; + return button.external === isExternal; } </script> <template> - <FetchData + <VnPaginate ref="dmsRef" + :data-key="$props.model" :url="$props.model" :filter="dmsFilter" - :where="{ [$props.filter]: route.params.id }" + :order="['dmsFk DESC']" + :auto-load="true" @on-fetch="setData" - auto-load - /> - <QTable - :columns="columns" - :rows="rows" - class="full-width q-mt-md" - hide-bottom - row-key="clientFk" - :grid="$q.screen.lt.sm" > - <template #body-cell="props"> - <QTd :props="props"> - <QTr :props="props"> - <component - v-if="props.col.component" - :is="props.col.component" - v-bind="props.col.props && props.col.props(props)" - > - <span - v-if="props.col.component == 'span'" - style="white-space: wrap" - >{{ props.value }}</span - > - </component> - </QTr> - - <div class="flex justify-center" v-if="props.col.name == 'options'"> - <div v-for="button of props.col.components" :key="button.id"> - <component - v-if="shouldRenderButton(button.name, props.row.isDocuware)" - :is="button.component" - v-bind="button.props(props)" - @click="button.click(props)" - /> - </div> - </div> - </QTd> - </template> - <template #item="props"> - <div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition"> - <QCard - bordered - flat - @keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()" - > - <QSeparator /> - <QList dense> - <QItem v-for="col in props.cols" :key="col.name"> - <div v-if="col.name != 'options'" class="row"> - <span class="labelColor">{{ col.label }}:</span> - <span>{{ col.value }}</span> - </div> - <div v-if="col.name == 'options'" class="row"> - <div - v-for="button of col.components" - :key="button.id" - class="row" + <template #body> + <QTable + :columns="columns" + :rows="rows" + class="full-width q-mt-md" + hide-bottom + row-key="clientFk" + :grid="$q.screen.lt.sm" + > + <template #body-cell="props"> + <QTd :props="props"> + <QTr :props="props"> + <component + v-if="props.col.component" + :is="props.col.component" + v-bind="props.col.props && props.col.props(props)" + > + <span + v-if="props.col.component == 'span'" + style="white-space: wrap" + >{{ props.value }}</span > - <component - v-if=" - shouldRenderButton( - button.name, - props.row.isDocuware - ) - " - :is="button.component" - v-bind="button.props(col)" - @click="button.click(col)" - /> - </div> + </component> + </QTr> + + <div class="row no-wrap" v-if="props.col.name == 'options'"> + <div v-for="button of props.col.components" :key="button.id"> + <component + v-if=" + shouldRenderButton(button, props.row.isDocuware) + " + :is="button.component" + v-bind="button.props(props)" + @click="button.click(props)" + /> </div> - </QItem> - </QList> - </QCard> - </div> + </div> + </QTd> + </template> + <template #item="props"> + <div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition"> + <QCard + bordered + flat + @keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()" + > + <QSeparator /> + <QList dense> + <QItem v-for="col in props.cols" :key="col.name"> + <div v-if="col.name != 'options'" class="row"> + <span class="labelColor">{{ col.label }}:</span> + <span>{{ col.value }}</span> + </div> + <div v-if="col.name == 'options'" class="row"> + <div + v-for="button of col.components" + :key="button.id" + class="row" + > + <component + v-if=" + shouldRenderButton( + button.name, + props.row.isDocuware + ) + " + :is="button.component" + v-bind="button.props(col)" + @click="button.click(col)" + /> + </div> + </div> + </QItem> + </QList> + </QCard> + </div> + </template> + </QTable> </template> - </QTable> + </VnPaginate> <QDialog v-model="formDialog.show"> <VnDms :model="updateModel ?? model" diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue index d1b2f5ccb..4cb2e6d6e 100644 --- a/src/components/ui/VnPaginate.vue +++ b/src/components/ui/VnPaginate.vue @@ -110,7 +110,7 @@ async function paginate() { if (!arrayData.hasMoreData.value) { if (store.userParamsChanged) arrayData.hasMoreData.value = true; store.userParamsChanged = false; - isLoading.value = false; + endPagination(); return; } @@ -120,12 +120,14 @@ async function paginate() { pagination.value.sortBy = sortBy; pagination.value.descending = descending; - isLoading.value = false; + endPagination(); +} +function endPagination() { + isLoading.value = false; emit('onFetch', store.data); emit('onPaginate'); } - async function onLoad(index, done) { if (!store.data) { return done(); @@ -188,6 +190,12 @@ async function onLoad(index, done) { <QSpinner color="orange" size="md" /> </div> </QInfiniteScroll> + <div + v-if="!isLoading && arrayData.hasMoreData" + class="w-full flex justify-center q-mt-md" + > + <QBtn color="primary" :label="t('Load more data')" @click="paginate()" /> + </div> </template> <style lang="scss" scoped> @@ -204,4 +212,5 @@ async function onLoad(index, done) { es: No data to display: Sin datos que mostrar No results found: No se han encontrado resultados + Load more data: Cargar más resultados </i18n> diff --git a/src/components/ui/VnTree.vue b/src/components/ui/VnTree.vue index 9a99124c6..13aa05635 100644 --- a/src/components/ui/VnTree.vue +++ b/src/components/ui/VnTree.vue @@ -76,7 +76,7 @@ const removeNode = (node) => { notify(t('department.departmentRemoved'), 'positive'); await fetchNodeLeaves(parentFk); } catch (err) { - console.log('Error removing department'); + console.error('Error removing department'); } }); }; diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js index 45162f81b..12639dcd6 100644 --- a/src/composables/downloadFile.js +++ b/src/composables/downloadFile.js @@ -4,9 +4,8 @@ import { getUrl } from './getUrl'; const { getTokenMultimedia } = useSession(); const token = getTokenMultimedia(); -export async function downloadFile(dmsId, isDocuware = false) { +export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) { let appUrl = await getUrl('', 'lilium'); appUrl = appUrl.replace('/#/', ''); - const urlPath = isDocuware ? '/docuwareDownload' : '/downloadFile'; - window.open(`${appUrl}/api/dms/${dmsId}${urlPath}?access_token=${token}`); + window.open(url ?? `${appUrl}/api/${model}/${id}${urlPath}?access_token=${token}`); } diff --git a/src/composables/getUrl.js b/src/composables/getUrl.js index 7b7103562..1e6aec4c6 100644 --- a/src/composables/getUrl.js +++ b/src/composables/getUrl.js @@ -1,7 +1,4 @@ import axios from 'axios'; -import useNotify from 'src/composables/useNotify.js'; - -const { notify } = useNotify(); export async function getUrl(route, app = 'salix') { let url; @@ -11,24 +8,3 @@ export async function getUrl(route, app = 'salix') { }); return url; } - -export async function getUrlFindOne(route, app = 'salix') { - try { - const env = process.env.NODE_ENV; - - const filter = { - where: { and: [{ appName: app }, { environment: env }] }, - }; - - const { data } = await axios.get('Urls/findOne', { - params: { filter: JSON.stringify(filter) }, - }); - - if (!data) return null; - - return data.url + route; - } catch (err) { - notify('Direction not found', 'negative'); - console.error('Error finding url: ', err); - } -} diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index b6b81f2d5..d32eeb32e 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -98,7 +98,7 @@ export function useArrayData(key, userOptions) { const { limit } = filter; - hasMoreData.value = response.data.length === limit; + hasMoreData.value = response.data.length >= limit; if (append) { if (!store.data) store.data = []; diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index ff86aec4c..793fc42fd 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -92,6 +92,8 @@ export default { log: 'Logs', parkingList: 'Parkings list', }, + created: 'Created', + worker: 'Worker', }, errors: { statusUnauthorized: 'Access denied', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index fbf8bc230..74e289039 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -92,6 +92,8 @@ export default { log: 'Historial', parkingList: 'Listado de parkings', }, + created: 'Fecha creación', + worker: 'Trabajador', }, errors: { statusUnauthorized: 'Acceso denegado', diff --git a/src/pages/Department/Card/DepartmentDescriptor.vue b/src/pages/Department/Card/DepartmentDescriptor.vue index 7049d4145..e60a8c91b 100644 --- a/src/pages/Department/Card/DepartmentDescriptor.vue +++ b/src/pages/Department/Card/DepartmentDescriptor.vue @@ -63,7 +63,7 @@ const removeDepartment = () => { router.push({ name: 'WorkerDepartment' }); notify('department.departmentRemoved', 'positive'); } catch (err) { - console.log('Error removing department'); + console.error('Error removing department'); } }); }; diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue index d0c5cf5e5..aef18d138 100644 --- a/src/pages/Worker/Card/WorkerDms.vue +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -1,11 +1,14 @@ <script setup> import VnDmsList from 'src/components/common/VnDmsList.vue'; +import { useRoute } from 'vue-router'; +const route = useRoute(); </script> <template> <VnDmsList - model="WorkerDms" + :model="`WorkerDms/${route.params.id}/filter`" update-model="Workers" + download-model="WorkerDms" default-dms-code="hhrrData" - filter="workerFk" + filter="worker" /> </template> From 0144aab7a5f613d0775f14d79d56eccd2ad60ac0 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Tue, 9 Apr 2024 08:51:00 -0300 Subject: [PATCH 09/15] Mini fix --- src/i18n/en/index.js | 4 ++-- src/i18n/es/index.js | 4 ++-- src/pages/Item/ItemList.vue | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 59082422a..fdeec2744 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -1228,14 +1228,14 @@ export default { description: 'Description', stems: 'Stems', category: 'Category', - type: 'Type', + typeName: 'Type', intrastat: 'Intrastat', isActive: 'Active', size: 'Size', origin: 'Origin', userName: 'Buyer', weightByPiece: 'Weight/Piece', - multiplier: 'Multiplier', + stemMultiplier: 'Multiplier', producer: 'Producer', landed: 'Landed', }, diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 16b2e9394..bc9cb0d01 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -1227,14 +1227,14 @@ export default { description: 'Descripción', stems: 'Tallos', category: 'Reino', - type: 'Tipo', + typeName: 'Tipo', intrastat: 'Intrastat', isActive: 'Activo', size: 'Medida', origin: 'Origen', weightByPiece: 'Peso (gramos)/tallo', userName: 'Comprador', - multiplier: 'Multiplicador', + stemMultiplier: 'Multiplicador', producer: 'Productor', landed: 'F. entrega', }, diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 304145e23..407b01fab 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -311,7 +311,7 @@ const columns = computed(() => [ format: (val) => dashIfEmpty(val), }, { - label: t('item.list.multiplier'), + label: t('item.list.stemMultiplier'), field: 'stemMultiplier', name: 'stemMultiplier', align: 'left', @@ -383,13 +383,15 @@ const cloneItem = async (itemFk) => { if (!data) return; router.push({ name: 'ItemTags', params: { id: data.id } }); } catch (err) { - console.error('Error cloning row', err); + console.error('Error cloning item', err); } }; onMounted(async () => { stateStore.rightDrawer = true; - const filteredColumns = columns.value.filter((col) => col.name !== 'picture'); + const filteredColumns = columns.value.filter( + (col) => col.name !== 'picture' && col.name !== 'actions' + ); allColumnNames.value = filteredColumns.map((col) => col.name); }); From 9c3407846b0ab23a432e26e33523f7fb98bf341f Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Tue, 9 Apr 2024 10:30:40 -0300 Subject: [PATCH 10/15] Fix translation --- src/pages/Item/ItemList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index 407b01fab..ec54d1c05 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -195,7 +195,7 @@ const columns = computed(() => [ }, }, { - label: t('item.list.type'), + label: t('item.list.typeName'), field: 'typeName', name: 'typeName', align: 'left', From e88194eb88235bca0a8b2a328e3b1a03a2d7393d Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Tue, 9 Apr 2024 16:00:12 -0300 Subject: [PATCH 11/15] Update worker dms model prop --- src/pages/Worker/Card/WorkerDms.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue index aef18d138..eee42857c 100644 --- a/src/pages/Worker/Card/WorkerDms.vue +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -1,11 +1,10 @@ <script setup> import VnDmsList from 'src/components/common/VnDmsList.vue'; -import { useRoute } from 'vue-router'; -const route = useRoute(); </script> + <template> <VnDmsList - :model="`WorkerDms/${route.params.id}/filter`" + model="WorkerDms" update-model="Workers" download-model="WorkerDms" default-dms-code="hhrrData" From 39af4278a688309d5543f56a84da523a986f040f Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 10 Apr 2024 08:27:30 -0300 Subject: [PATCH 12/15] Add delete model to worker dms --- src/components/common/VnDmsList.vue | 6 +++++- src/pages/Worker/Card/WorkerDms.vue | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index 8c3ffc807..cc184703d 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -28,6 +28,10 @@ const $props = defineProps({ type: String, default: null, }, + deleteModel: { + type: String, + default: null, + }, downloadModel: { type: String, required: false, @@ -240,7 +244,7 @@ function deleteDms(dmsFk) { }, }) .onOk(async () => { - await axios.post(`${$props.model}/${dmsFk}/removeFile`); + await axios.post($props.deleteModel ?? `${$props.model}/${dmsFk}/removeFile`); const index = rows.value.findIndex((row) => row.id == dmsFk); rows.value.splice(index, 1); }); diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue index eee42857c..faf89b245 100644 --- a/src/pages/Worker/Card/WorkerDms.vue +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -1,11 +1,13 @@ <script setup> import VnDmsList from 'src/components/common/VnDmsList.vue'; +import { useRoute } from 'vue-router'; +const route = useRoute(); </script> - <template> <VnDmsList - model="WorkerDms" + :model="`WorkerDms/${route.params.id}/filter`" update-model="Workers" + :delete-model="`WorkerDms/${route.params.id}/removeFile`" download-model="WorkerDms" default-dms-code="hhrrData" filter="worker" From 9ae79a5631639f023f175eb7d8996107a3394f82 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 10 Apr 2024 08:43:57 -0300 Subject: [PATCH 13/15] Add translation again --- src/i18n/es/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 6d54e52ec..c19eb0764 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -865,6 +865,7 @@ export default { basicData: 'Datos básicos', notes: 'Notas', pda: 'PDA', + dms: 'Mi documentación', notifications: 'Notificaciones', pbx: 'Centralita', log: 'Historial', From e676933942d9987894360c93e6dbbe021897c932 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 10 Apr 2024 08:59:05 -0300 Subject: [PATCH 14/15] Update worker dms remove --- src/components/common/VnDmsList.vue | 2 +- src/pages/Worker/Card/WorkerDms.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index cc184703d..f70146c58 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -244,7 +244,7 @@ function deleteDms(dmsFk) { }, }) .onOk(async () => { - await axios.post($props.deleteModel ?? `${$props.model}/${dmsFk}/removeFile`); + await axios.post(`${$props.deleteModel ?? $props.model}/${dmsFk}/removeFile`); const index = rows.value.findIndex((row) => row.id == dmsFk); rows.value.splice(index, 1); }); diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue index faf89b245..a4f7ef5a9 100644 --- a/src/pages/Worker/Card/WorkerDms.vue +++ b/src/pages/Worker/Card/WorkerDms.vue @@ -7,7 +7,7 @@ const route = useRoute(); <VnDmsList :model="`WorkerDms/${route.params.id}/filter`" update-model="Workers" - :delete-model="`WorkerDms/${route.params.id}/removeFile`" + delete-model="WorkerDms" download-model="WorkerDms" default-dms-code="hhrrData" filter="worker" From 99c576b16eb146fb56671d6323c2712bec52fc05 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 10 Apr 2024 09:32:15 -0300 Subject: [PATCH 15/15] Add actions again --- src/pages/Item/ItemList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index ec54d1c05..3e399e29f 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -436,7 +436,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :all-columns="allColumnNames" table-code="itemsIndex" labels-traductions-path="item.list" - @on-config-saved="visibleColumns = ['picture', ...$event]" + @on-config-saved="visibleColumns = ['picture', ...$event, 'actions']" /> </div> <QSpace />