diff --git a/src/boot/axios.js b/src/boot/axios.js index e3e7289af..4fd83ddea 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -1,11 +1,10 @@ import axios from 'axios'; -import { Notify } from 'quasar'; import { useSession } from 'src/composables/useSession'; import { Router } from 'src/router'; -import { i18n } from './i18n'; +import useNotify from 'src/composables/useNotify.js'; const session = useSession(); -const { t } = i18n.global; +const { notify } = useNotify(); axios.defaults.baseURL = '/api/'; @@ -27,10 +26,7 @@ const onResponse = (response) => { const isSaveRequest = method === 'patch'; if (isSaveRequest) { - Notify.create({ - message: t('globals.dataSaved'), - type: 'positive', - }); + notify('globals.dataSaved', 'positive'); } return response; @@ -67,10 +63,7 @@ const onResponseError = (error) => { return Promise.reject(error); } - Notify.create({ - message: t(message), - type: 'negative', - }); + notify(message, 'negative'); return Promise.reject(error); }; diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue index 05c63d563..f10dee61a 100644 --- a/src/components/FormModel.vue +++ b/src/components/FormModel.vue @@ -83,6 +83,10 @@ const $props = defineProps({ default: '', description: 'It is used for redirect on click "save and continue"', }, + reload: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(['onFetch', 'onDataSaved']); const modelValue = computed( @@ -201,6 +205,7 @@ async function save() { if ($props.urlCreate) notify('globals.dataCreated', 'positive'); updateAndEmit('onDataSaved', formData.value, response?.data); + if ($props.reload) await arrayData.fetch({}); } catch (err) { console.error(err); notify('errors.writeRequest', 'negative'); diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index cab5b98be..ceff9028a 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -22,6 +22,10 @@ const props = defineProps({ type: String, default: '', }, + moduleName: { + type: String, + default: null, + }, }); const emit = defineEmits(['onFetch']); const route = useRoute(); @@ -83,7 +87,7 @@ function existSummary(routes) { v-if="showRedirectToSummaryIcon" class="header link" :to="{ - name: `${route.meta.moduleName}Summary`, + name: `${moduleName ?? route.meta.moduleName}Summary`, params: { id: entityId || entity.id }, }" > diff --git a/src/pages/Claim/Card/ClaimBasicData.vue b/src/pages/Claim/Card/ClaimBasicData.vue index 50c9502d2..977a4dc5b 100644 --- a/src/pages/Claim/Card/ClaimBasicData.vue +++ b/src/pages/Claim/Card/ClaimBasicData.vue @@ -17,26 +17,6 @@ const { t } = useI18n(); const { getTokenMultimedia } = useSession(); const token = getTokenMultimedia(); -const claimFilter = { - fields: [ - 'id', - 'clientFk', - 'created', - 'workerFk', - 'claimStateFk', - 'packages', - 'pickup', - ], - include: [ - { - relation: 'client', - scope: { - fields: ['name'], - }, - }, - ], -}; - const claimStates = ref([]); const claimStatesCopy = ref([]); const optionsList = ref([]); @@ -87,11 +67,10 @@ const statesFilter = { /> <FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load /> <FormModel - :url="`Claims/${route.params.id}`" + model="Claim" :url-update="`Claims/updateClaim/${route.params.id}`" - :filter="claimFilter" - model="claim" auto-load + :reload="true" > <template #form="{ data, validate, filter }"> <VnRow class="row q-gutter-md q-mb-md"> diff --git a/src/pages/Claim/Card/ClaimCard.vue b/src/pages/Claim/Card/ClaimCard.vue index f38ab5484..19d63e3b2 100644 --- a/src/pages/Claim/Card/ClaimCard.vue +++ b/src/pages/Claim/Card/ClaimCard.vue @@ -2,6 +2,7 @@ import VnCard from 'components/common/VnCard.vue'; import ClaimDescriptor from './ClaimDescriptor.vue'; import ClaimFilter from '../ClaimFilter.vue'; +import filter from './ClaimFilter.js'; </script> <template> <VnCard @@ -13,5 +14,6 @@ import ClaimFilter from '../ClaimFilter.vue'; search-url="Claims/filter" searchbar-label="Search claim" searchbar-info="You can search by claim id or customer name" + :filter="filter" /> </template> diff --git a/src/pages/Claim/Card/ClaimDescriptor.vue b/src/pages/Claim/Card/ClaimDescriptor.vue index aae999117..5336c4427 100644 --- a/src/pages/Claim/Card/ClaimDescriptor.vue +++ b/src/pages/Claim/Card/ClaimDescriptor.vue @@ -12,6 +12,7 @@ import useCardDescription from 'src/composables/useCardDescription'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; import { getUrl } from 'src/composables/getUrl'; import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; +import filter from './ClaimFilter.js'; const $props = defineProps({ id: { @@ -29,49 +30,6 @@ const entityId = computed(() => { return $props.id || route.params.id; }); -const filter = { - include: [ - { - relation: 'client', - scope: { - include: [ - { relation: 'salesPersonUser' }, - { - relation: 'claimsRatio', - scope: { - fields: ['claimingRate'], - limit: 1, - }, - }, - ], - }, - }, - { - relation: 'claimState', - }, - { - relation: 'ticket', - scope: { - include: [ - { relation: 'zone' }, - { - relation: 'address', - scope: { - include: { relation: 'province' }, - }, - }, - ], - }, - }, - { - relation: 'worker', - scope: { - include: { relation: 'user' }, - }, - }, - ], -}; - const STATE_COLOR = { pending: 'warning', incomplete: 'info', @@ -101,7 +59,7 @@ onMounted(async () => { :title="data.title" :subtitle="data.subtitle" @on-fetch="setData" - data-key="claimData" + data-key="Claim" > <template #menu="{ entity }"> <ClaimDescriptorMenu :claim="entity" /> diff --git a/src/pages/Claim/Card/ClaimFilter.js b/src/pages/Claim/Card/ClaimFilter.js new file mode 100644 index 000000000..50cabe228 --- /dev/null +++ b/src/pages/Claim/Card/ClaimFilter.js @@ -0,0 +1,52 @@ +export default { + fields: [ + 'id', + 'clientFk', + 'created', + 'workerFk', + 'claimStateFk', + 'packages', + 'pickup', + 'ticketFk', + ], + include: [ + { + relation: 'client', + scope: { + include: [ + { relation: 'salesPersonUser' }, + { + relation: 'claimsRatio', + scope: { + fields: ['claimingRate'], + limit: 1, + }, + }, + ], + }, + }, + { + relation: 'claimState', + }, + { + relation: 'ticket', + scope: { + include: [ + { relation: 'zone' }, + { + relation: 'address', + scope: { + include: { relation: 'province' }, + }, + }, + ], + }, + }, + { + relation: 'worker', + scope: { + include: { relation: 'user' }, + }, + }, + ], +}; diff --git a/src/pages/Customer/Defaulter/CustomerDefaulter.vue b/src/pages/Customer/Defaulter/CustomerDefaulter.vue index af7ce0a26..06732b944 100644 --- a/src/pages/Customer/Defaulter/CustomerDefaulter.vue +++ b/src/pages/Customer/Defaulter/CustomerDefaulter.vue @@ -22,7 +22,7 @@ const balanceDueTotal = ref(0); const selected = ref([]); const tableColumnComponents = { - client: { + clientFk: { component: QBtn, props: () => ({ flat: true, class: 'link', noCaps: true }), event: () => {}, @@ -40,7 +40,7 @@ const tableColumnComponents = { props: () => ({ flat: true, class: 'link', noCaps: true }), event: () => {}, }, - department: { + departmentName: { component: 'span', props: () => {}, event: () => {}, @@ -102,12 +102,12 @@ const columns = computed(() => [ align: 'left', field: 'clientName', label: t('Client'), - name: 'client', + name: 'clientFk', sortable: true, }, { align: 'left', - field: 'isWorker', + field: ({ isWorker }) => Boolean(isWorker), label: t('Is worker'), name: 'isWorker', }, @@ -122,7 +122,7 @@ const columns = computed(() => [ align: 'left', field: 'departmentName', label: t('Department'), - name: 'department', + name: 'departmentName', sortable: true, }, { @@ -204,48 +204,24 @@ const viewAddObservation = (rowsSelected) => { }); }; -const departments = ref(new Map()); - const onFetch = async (data) => { - const salesPersonFks = data.map((item) => item.salesPersonFk); - const departmentNames = salesPersonFks.map(async (salesPersonFk) => { - try { - const { data: workerDepartment } = await axios.get( - `WorkerDepartments/${salesPersonFk}` - ); - const { data: department } = await axios.get( - `Departments/${workerDepartment.departmentFk}` - ); - departments.value.set(salesPersonFk, department.name); - } catch (error) { - console.error('Err: ', error); - } - }); const recoveryData = await axios.get('Recoveries'); - const recoveries = recoveryData.data.map(({ clientFk, finished }) => ({ clientFk, finished, })); - await Promise.all(departmentNames); - data.forEach((item) => { - item.departmentName = departments.value.get(item.salesPersonFk); - item.isWorker = item.businessTypeFk === 'worker'; const recovery = recoveries.find(({ clientFk }) => clientFk === item.clientFk); item.finished = recovery?.finished === null; }); - - for (const element of data) element.isWorker = element.businessTypeFk === 'worker'; - balanceDueTotal.value = data.reduce((acc, { amount = 0 }) => acc + amount, 0); }; function exprBuilder(param, value) { switch (param) { case 'clientFk': - return { [`d.${param}`]: value?.id }; + return { [`d.${param}`]: value }; case 'creditInsurance': case 'amount': case 'workerFk': diff --git a/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue b/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue index 9220bd7c0..6b4d8baaa 100644 --- a/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue +++ b/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue @@ -1,7 +1,6 @@ <script setup> import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; - import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnInput from 'src/components/common/VnInput.vue'; @@ -16,14 +15,13 @@ const props = defineProps({ }, }); -const clients = ref(); const salespersons = ref(); const countries = ref(); const authors = ref(); +const departments = ref(); </script> <template> - <FetchData @on-fetch="(data) => (clients = data)" auto-load url="Clients" /> <FetchData :filter="{ where: { role: 'salesPerson' } }" @on-fetch="(data) => (salespersons = data)" @@ -36,6 +34,7 @@ const authors = ref(); auto-load url="Workers/activeWithInheritedRole" /> + <FetchData @on-fetch="(data) => (departments = data)" auto-load url="Departments" /> <VnFilterPanel :data-key="props.dataKey" :search-button="true"> <template #tags="{ tag, formatFn }"> @@ -47,29 +46,22 @@ const authors = ref(); <template #body="{ params, searchFn }"> <QItem class="q-mb-sm"> - <QItemSection v-if="clients"> - <VnSelect - :label="t('Client')" - :options="clients" - dense - emit-value - hide-selected - map-options - option-label="name" - option-value="id" - outlined - rounded - use-input - v-model="params.clientFk" - @update:model-value="searchFn()" - auto-load - /> - </QItemSection> - <QItemSection v-else> - <QSkeleton class="full-width" type="QInput" /> - </QItemSection> + <VnSelect + :label="t('Client')" + url="Clients" + dense + option-label="name" + option-value="id" + outlined + rounded + emit-value + hide-selected + map-options + v-model="params.clientFk" + use-input + @update:model-value="searchFn()" + /> </QItem> - <QItem class="q-mb-sm"> <QItemSection v-if="salespersons"> <VnSelect @@ -93,6 +85,29 @@ const authors = ref(); <QSkeleton class="full-width" type="QInput" /> </QItemSection> </QItem> + <QItem class="q-mb-sm"> + <QItemSection v-if="departments"> + <VnSelect + :input-debounce="0" + :label="t('Departments')" + :options="departments" + dense + emit-value + hide-selected + map-options + option-label="name" + option-value="id" + outlined + rounded + use-input + v-model="params.departmentFk" + @update:model-value="searchFn()" + /> + </QItemSection> + <QItemSection v-else> + <QSkeleton class="full-width" type="QInput" /> + </QItemSection> + </QItem> <QItem class="q-mb-sm"> <QItemSection v-if="countries"> diff --git a/src/pages/Department/Card/DepartmentDescriptorProxy.vue b/src/pages/Department/Card/DepartmentDescriptorProxy.vue index 07a6b6af1..5b556f655 100644 --- a/src/pages/Department/Card/DepartmentDescriptorProxy.vue +++ b/src/pages/Department/Card/DepartmentDescriptorProxy.vue @@ -1,6 +1,6 @@ <script setup> import DepartmentDescriptor from './DepartmentDescriptor.vue'; -import DepartmentSummaryDialog from './DepartmentSummaryDialog.vue'; +import DepartmentSummary from './DepartmentSummary.vue'; const $props = defineProps({ id: { @@ -15,7 +15,7 @@ const $props = defineProps({ <DepartmentDescriptor v-if="$props.id" :id="$props.id" - :summary="DepartmentSummaryDialog" + :summary="DepartmentSummary" /> </QPopupProxy> </template> diff --git a/src/pages/Department/Card/DepartmentSummary.vue b/src/pages/Department/Card/DepartmentSummary.vue index 6377ac779..ecbb39ed0 100644 --- a/src/pages/Department/Card/DepartmentSummary.vue +++ b/src/pages/Department/Card/DepartmentSummary.vue @@ -32,6 +32,7 @@ onMounted(async () => { :url="`Departments/${entityId}`" class="full-width" style="max-width: 900px" + module-name="Department" > <template #header="{ entity }"> <div>{{ entity.name }}</div> diff --git a/src/pages/Entry/Card/EntryBuys.vue b/src/pages/Entry/Card/EntryBuys.vue index b08c40810..6e66f4ce7 100644 --- a/src/pages/Entry/Card/EntryBuys.vue +++ b/src/pages/Entry/Card/EntryBuys.vue @@ -11,9 +11,9 @@ import VnInput from 'src/components/common/VnInput.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; import VnConfirm from 'components/ui/VnConfirm.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; +import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import { useQuasar } from 'quasar'; -import { useStateStore } from 'stores/useStateStore'; import { toCurrency } from 'src/filters'; import axios from 'axios'; import useNotify from 'src/composables/useNotify.js'; @@ -22,7 +22,6 @@ const quasar = useQuasar(); const route = useRoute(); const router = useRouter(); const { t } = useI18n(); -const stateStore = useStateStore(); const { notify } = useNotify(); const rowsSelected = ref([]); @@ -312,20 +311,22 @@ const lockIconType = (groupingMode, mode) => { auto-load @on-fetch="(data) => (packagingsOptions = data)" /> - <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()"> - <QBtnGroup push style="column-gap: 10px"> - <slot name="moreBeforeActions" /> - <QBtn - :label="t('globals.remove')" - color="primary" - icon="delete" - flat - @click="openRemoveDialog()" - :disable="!rowsSelected?.length" - :title="t('globals.remove')" - /> - </QBtnGroup> - </Teleport> + <VnSubToolbar> + <template #st-actions> + <QBtnGroup push style="column-gap: 10px"> + <slot name="moreBeforeActions" /> + <QBtn + :label="t('globals.remove')" + color="primary" + icon="delete" + flat + @click="openRemoveDialog()" + :disable="!rowsSelected?.length" + :title="t('globals.remove')" + /> + </QBtnGroup> + </template> + </VnSubToolbar> <VnPaginate ref="entryBuysPaginateRef" data-key="EntryBuys" diff --git a/src/pages/Route/Cmr/CmrList.vue b/src/pages/Route/Cmr/CmrList.vue index 07479a88d..327b37ac5 100644 --- a/src/pages/Route/Cmr/CmrList.vue +++ b/src/pages/Route/Cmr/CmrList.vue @@ -1,5 +1,5 @@ <script setup> -import { onBeforeMount, computed, ref } from 'vue'; +import { onBeforeMount, onMounted, computed, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { Notify } from 'quasar'; import axios from 'axios'; @@ -10,10 +10,12 @@ import CmrFilter from './CmrFilter.vue'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; +import { useStateStore } from 'src/stores/useStateStore'; const { t } = useI18n(); const { getTokenMultimedia } = useSession(); const token = getTokenMultimedia(); +const state = useStateStore(); const selected = ref([]); const warehouses = ref([]); @@ -81,6 +83,9 @@ onBeforeMount(async () => { const { data } = await axios.get('Warehouses'); warehouses.value = data; }); + +onMounted(() => (state.rightDrawer = true)); + function getApiUrl() { return new URL(window.location).origin; } diff --git a/src/pages/Ticket/Card/TicketLog.vue b/src/pages/Ticket/Card/TicketLog.vue new file mode 100644 index 000000000..94b63a117 --- /dev/null +++ b/src/pages/Ticket/Card/TicketLog.vue @@ -0,0 +1,7 @@ +<script setup> +import VnLog from 'src/components/common/VnLog.vue'; +</script> + +<template> + <VnLog model="Ticket" url="/TicketLogs"></VnLog> +</template> diff --git a/src/pages/Travel/Card/TravelLog.vue b/src/pages/Travel/Card/TravelLog.vue index 1a2667ff7..a1f774769 100644 --- a/src/pages/Travel/Card/TravelLog.vue +++ b/src/pages/Travel/Card/TravelLog.vue @@ -2,5 +2,5 @@ import VnLog from 'src/components/common/VnLog.vue'; </script> <template> - <VnLog model="Entry" url="/TravelLogs"></VnLog> + <VnLog model="Travel" url="/TravelLogs"></VnLog> </template> diff --git a/src/router/modules/department.js b/src/router/modules/department.js index dfd5e64ba..9aab40534 100644 --- a/src/router/modules/department.js +++ b/src/router/modules/department.js @@ -9,7 +9,7 @@ export default { moduleName: 'Department', }, component: RouterView, - redirect: { name: 'DepartmentCard' }, + redirect: { name: 'WorkerDepartment' }, menus: { main: [], card: ['DepartmentBasicData'], diff --git a/src/router/modules/ticket.js b/src/router/modules/ticket.js index 6cb7291dc..71b926308 100644 --- a/src/router/modules/ticket.js +++ b/src/router/modules/ticket.js @@ -12,7 +12,7 @@ export default { redirect: { name: 'TicketMain' }, menus: { main: ['TicketList'], - card: ['TicketBoxing', 'TicketSms', 'TicketSale'], + card: ['TicketBoxing', 'TicketSms', 'TicketSale', 'TicketLog'], }, children: [ { @@ -93,6 +93,15 @@ export default { }, component: () => import('src/pages/Ticket/Card/TicketSms.vue'), }, + { + path: 'log', + name: 'TicketLog', + meta: { + title: 'log', + icon: 'history', + }, + component: () => import('src/pages/Ticket/Card/TicketLog.vue'), + }, ], }, ],