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