refactor: refs #8197 adapt Ticket to VnCardMain
gitea/salix-front/pipeline/pr-beta This commit looks good Details

This commit is contained in:
Alex Moreno 2024-12-18 14:22:19 +01:00
parent 84ac4dd210
commit a940eb9861
3 changed files with 413 additions and 417 deletions

View File

@ -1,23 +1,7 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import VnCardBeta from 'components/common/VnCardBeta.vue';
import VnCard from 'components/common/VnCard.vue';
import TicketDescriptor from './TicketDescriptor.vue'; import TicketDescriptor from './TicketDescriptor.vue';
import TicketFilter from '../TicketFilter.vue';
const { t } = useI18n();
</script> </script>
<template> <template>
<VnCard <VnCardBeta data-key="Ticket" base-url="Tickets" :descriptor="TicketDescriptor" />
data-key="Ticket"
base-url="Tickets"
:filter-panel="TicketFilter"
:descriptor="TicketDescriptor"
search-data-key="TicketList"
:searchbar-props="{
url: 'Tickets/filter',
label: t('card.search'),
info: t('card.searchInfo'),
}"
/>
</template> </template>

View File

@ -8,13 +8,11 @@ import { useQuasar } from 'quasar';
import { toDate, toCurrency, dashIfEmpty } from 'src/filters/index'; import { toDate, toCurrency, dashIfEmpty } from 'src/filters/index';
import useNotify from 'src/composables/useNotify'; import useNotify from 'src/composables/useNotify';
import TicketSummary from './Card/TicketSummary.vue'; import TicketSummary from './Card/TicketSummary.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import VnTable from 'src/components/VnTable/VnTable.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnRow from 'src/components/ui/VnRow.vue'; import VnRow from 'src/components/ui/VnRow.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import TicketFilter from './TicketFilter.vue'; import TicketFilter from './TicketFilter.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
@ -23,6 +21,7 @@ import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import { toTimeFormat } from 'src/filters/date'; import { toTimeFormat } from 'src/filters/date';
import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
import TicketProblems from 'src/components/TicketProblems.vue'; import TicketProblems from 'src/components/TicketProblems.vue';
import VnSection from 'src/components/common/VnSection.vue';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -66,6 +65,7 @@ const dialogData = ref();
const companiesOptions = ref([]); const companiesOptions = ref([]);
const accountingOptions = ref([]); const accountingOptions = ref([]);
const amountToReturn = ref(); const amountToReturn = ref();
const dataKey = 'TicketList';
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -452,223 +452,228 @@ function setReference(data) {
@on-fetch="(data) => (accountingOptions = data)" @on-fetch="(data) => (accountingOptions = data)"
auto-load auto-load
/> />
<VnSearchbar <VnSection
data-key="TicketList" :data-key="dataKey"
:label="t('Search ticket')" :columns="columns"
:info="t('You can search by ticket id or alias')" prefix="card"
data-cy="ticketListSearchBar" :array-data-props="{
/> url: 'Tickets/filter',
<RightMenu> order: ['shippedDate DESC', 'shippedHour ASC', 'zoneLanding ASC', 'id'],
<template #right-panel> exprBuilder,
}"
>
<template #rightMenu>
<TicketFilter data-key="TicketList" /> <TicketFilter data-key="TicketList" />
</template> </template>
</RightMenu> <template #body>
<VnTable <VnTable
ref="tableRef" ref="tableRef"
data-key="TicketList" :data-key="dataKey"
url="Tickets/filter" :create="{
:create="{ urlCreate: 'Tickets/new',
urlCreate: 'Tickets/new', title: t('ticketList.createTicket'),
title: t('ticketList.createTicket'), onDataSaved: ({ id }) => tableRef.redirect(id),
onDataSaved: ({ id }) => tableRef.redirect(id), formInitialData: { clientId: null },
formInitialData: { clientId: null }, }"
}" default-mode="table"
default-mode="table" :columns="columns"
:order="['shippedDate DESC', 'shippedHour ASC', 'zoneLanding ASC', 'id']" :user-params="userParams"
:columns="columns" :right-search="false"
:user-params="userParams" redirect="ticket"
:right-search="false" v-model:selected="selectedRows"
redirect="ticket" :table="{
v-model:selected="selectedRows" 'row-key': 'id',
:table="{ selection: 'multiple',
'row-key': 'id', }"
selection: 'multiple', data-cy="ticketListTable"
}"
data-cy="ticketListTable"
>
<template #column-statusIcons="{ row }">
<TicketProblems :row="row" />
</template>
<template #column-salesPersonFk="{ row }">
<span class="link" @click.stop>
{{ dashIfEmpty(row.userName) }}
<CustomerDescriptorProxy :id="row.salesPersonFk" />
</span>
</template>
<template #column-shippedDate="{ row }">
<span v-if="getDateColor(row.shipped)">
<QChip :class="getDateColor(row.shipped)" dense square>
{{ toDate(row.shippedDate) }}
</QChip>
</span>
</template>
<template #column-nickname="{ row }">
<span class="link" @click.stop>
{{ row.nickname }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #column-addressNickname="{ row }">
<span class="link" @click.stop>
{{ row.addressNickname }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #column-stateFk="{ row }">
<span v-if="row.refFk">
<span class="link" @click.stop>
{{ row.refFk }}
<InvoiceOutDescriptorProxy :id="row.invoiceOutId" />
</span>
</span>
<span v-else-if="getColor(row)">
<QChip :class="getColor(row)" dense square>
{{ row.state }}
</QChip>
</span>
<span v-else>
{{ row.state }}
</span>
</template>
<template #column-zoneFk="{ row }">
<span class="link" @click.stop>
{{ dashIfEmpty(row.zoneName) }}
<ZoneDescriptorProxy :id="row.zoneFk" />
</span>
</template>
<template #column-totalWithVat="{ row }">
<QChip
v-if="row.totalWithVat > 0 && row.totalWithVat < 50"
class="bg-warning"
dense
square
> >
{{ row.totalWithVat }} <template #column-statusIcons="{ row }">
</QChip> <TicketProblems :row="row" />
</template> </template>
<template #more-create-dialog="{ data }"> <template #column-salesPersonFk="{ row }">
<VnRow> <span class="link" @click.stop>
<VnSelect {{ dashIfEmpty(row.userName) }}
url="Clients" <CustomerDescriptorProxy :id="row.salesPersonFk" />
:fields="['id', 'name']" </span>
:label="t('ticketList.client')" </template>
v-model="data.clientId" <template #column-shippedDate="{ row }">
:options="clientsOptions" <span v-if="getDateColor(row.shipped)">
option-value="id" <QChip :class="getDateColor(row.shipped)" dense square>
option-label="name" {{ toDate(row.shippedDate) }}
hide-selected </QChip>
required </span>
@update:model-value="(client) => onClientSelected(data)" </template>
:sort-by="'id ASC'" <template #column-nickname="{ row }">
> <span class="link" @click.stop>
<template #option="scope"> {{ row.nickname }}
<QItem v-bind="scope.itemProps"> <CustomerDescriptorProxy :id="row.clientFk" />
<QItemSection> </span>
<QItemLabel> </template>
{{ scope.opt.name }} <template #column-addressNickname="{ row }">
</QItemLabel> <span class="link" @click.stop>
<QItemLabel caption> {{ row.addressNickname }}
{{ `#${scope.opt.id}` }} <CustomerDescriptorProxy :id="row.clientFk" />
</QItemLabel> </span>
</QItemSection> </template>
</QItem> <template #column-stateFk="{ row }">
</template> <span v-if="row.refFk">
</VnSelect> <span class="link" @click.stop>
</VnRow> {{ row.refFk }}
<VnRow> <InvoiceOutDescriptorProxy :id="row.invoiceOutId" />
<VnSelect </span>
:label="t('basicData.address')" </span>
v-model="data.addressId" <span v-else-if="getColor(row)">
:options="addressesOptions" <QChip :class="getColor(row)" dense square>
option-value="id" {{ row.state }}
option-label="nickname" </QChip>
hide-selected </span>
map-options <span v-else>
required {{ row.state }}
:disable="!data.clientId" </span>
:sort-by="'isActive DESC'" </template>
@update:model-value="() => fetchAvailableAgencies(data)" <template #column-zoneFk="{ row }">
> <span class="link" @click.stop>
<template #option="scope"> {{ dashIfEmpty(row.zoneName) }}
<QItem <ZoneDescriptorProxy :id="row.zoneFk" />
v-bind="scope.itemProps" </span>
:class="{ disabled: !scope.opt.isActive }" </template>
<template #column-totalWithVat="{ row }">
<QChip
v-if="row.totalWithVat > 0 && row.totalWithVat < 50"
class="bg-warning"
dense
square
>
{{ row.totalWithVat }}
</QChip>
</template>
<template #more-create-dialog="{ data }">
<VnRow>
<VnSelect
url="Clients"
:fields="['id', 'name']"
:label="t('ticketList.client')"
v-model="data.clientId"
:options="clientsOptions"
option-value="id"
option-label="name"
hide-selected
required
@update:model-value="(client) => onClientSelected(data)"
:sort-by="'id ASC'"
> >
<QItemSection style="min-width: min-content" avatar> <template #option="scope">
<QIcon <QItem v-bind="scope.itemProps">
v-if=" <QItemSection>
scope.opt.isActive && <QItemLabel>
selectedClient?.defaultAddressFk === scope.opt.id {{ scope.opt.name }}
" </QItemLabel>
size="sm" <QItemLabel caption>
color="grey" {{ `#${scope.opt.id}` }}
name="star" </QItemLabel>
class="fill-icon" </QItemSection>
/> </QItem>
</QItemSection> </template>
<QItemSection> </VnSelect>
<QItemLabel </VnRow>
:class="{ <VnRow>
'color-vn-label': !scope.opt?.isActive, <VnSelect
}" :label="t('basicData.address')"
v-model="data.addressId"
:options="addressesOptions"
option-value="id"
option-label="nickname"
hide-selected
map-options
required
:disable="!data.clientId"
:sort-by="'isActive DESC'"
@update:model-value="() => fetchAvailableAgencies(data)"
>
<template #option="scope">
<QItem
v-bind="scope.itemProps"
:class="{ disabled: !scope.opt.isActive }"
> >
{{ <QItemSection style="min-width: min-content" avatar>
`${ <QIcon
!scope.opt?.isActive v-if="
? t('basicData.inactive') scope.opt.isActive &&
: '' selectedClient?.defaultAddressFk ===
} ` scope.opt.id
}} "
<span> size="sm"
{{ scope.opt?.nickname }}: color="grey"
{{ scope.opt?.street }}, {{ scope.opt?.city }} name="star"
</span> class="fill-icon"
</QItemLabel> />
</QItemSection> </QItemSection>
</QItem> <QItemSection>
</template> <QItemLabel
</VnSelect> :class="{
</VnRow> 'color-vn-label': !scope.opt?.isActive,
<VnRow> }"
<div class="col"> >
<VnInputDate {{
placeholder="dd-mm-aaa" `${
:label="t('globals.landed')" !scope.opt?.isActive
v-model="data.landed" ? t('basicData.inactive')
@update:model-value="() => fetchAvailableAgencies(data)" : ''
/> } `
</div> }}
</VnRow> <span>
<VnRow> {{ scope.opt?.nickname }}:
<div class="col"> {{ scope.opt?.street }},
<VnSelect {{ scope.opt?.city }}
url="Warehouses" </span>
:sort-by="['name']" </QItemLabel>
:label="t('globals.warehouse')" </QItemSection>
v-model="data.warehouseId" </QItem>
:options="warehousesOptions" </template>
option-value="id" </VnSelect>
option-label="name" </VnRow>
hide-selected <VnRow>
required <div class="col">
@update:model-value="() => fetchAvailableAgencies(data)" <VnInputDate
/> placeholder="dd-mm-aaa"
</div> :label="t('globals.landed')"
</VnRow> v-model="data.landed"
<VnRow> @update:model-value="() => fetchAvailableAgencies(data)"
<div class="col"> />
<VnSelect </div>
:label="t('globals.agency')" </VnRow>
v-model="data.agencyModeId" <VnRow>
:options="agenciesOptions" <div class="col">
option-value="agencyModeFk" <VnSelect
option-label="agencyMode" url="Warehouses"
hide-selected :sort-by="['name']"
/> :label="t('globals.warehouse')"
</div> v-model="data.warehouseId"
</VnRow> :options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
required
@update:model-value="() => fetchAvailableAgencies(data)"
/>
</div>
</VnRow>
<VnRow>
<div class="col">
<VnSelect
:label="t('globals.agency')"
v-model="data.agencyModeId"
:options="agenciesOptions"
option-value="agencyModeFk"
option-label="agencyMode"
hide-selected
/>
</div>
</VnRow>
</template>
</VnTable>
</template> </template>
</VnTable> </VnSection>
<QPageSticky :offset="[20, 80]" style="z-index: 2"> <QPageSticky :offset="[20, 80]" style="z-index: 2">
<QBtn <QBtn
v-if="hasSelectedRows" v-if="hasSelectedRows"

View File

@ -1,19 +1,12 @@
import { RouterView } from 'vue-router'; import { RouterView } from 'vue-router';
export default { const ticketCard = {
name: 'Ticket', name: 'TicketCard',
path: '/ticket', path: ':id',
component: () => import('src/pages/Ticket/Card/TicketCard.vue'),
redirect: { name: 'TicketSummary' },
meta: { meta: {
title: 'tickets', menu: [
icon: 'vn:ticket',
moduleName: 'Ticket',
keyBinding: 't',
},
component: RouterView,
redirect: { name: 'TicketMain' },
menus: {
main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'],
card: [
'TicketBasicData', 'TicketBasicData',
'TicketSale', 'TicketSale',
'TicketLog', 'TicketLog',
@ -32,21 +25,200 @@ export default {
'TicketSms', 'TicketSms',
], ],
}, },
children: [
{
path: 'summary',
name: 'TicketSummary',
meta: {
title: 'summary',
icon: 'launch',
},
component: () => import('src/pages/Ticket/Card/TicketSummary.vue'),
},
{
path: 'basic-data',
name: 'TicketBasicData',
meta: {
title: 'basicData',
icon: 'vn:settings',
},
component: () =>
import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'),
},
{
path: 'sale',
name: 'TicketSale',
meta: {
title: 'sale',
icon: 'vn:lines',
},
component: () => import('src/pages/Ticket/Card/TicketSale.vue'),
},
{
path: 'request',
name: 'TicketPurchaseRequest',
meta: {
title: 'purchaseRequest',
icon: 'vn:buyrequest',
},
component: () => import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'),
},
{
path: 'tracking',
name: 'TicketTracking',
meta: {
title: 'tracking',
icon: 'vn:eye',
},
component: () => import('src/pages/Ticket/Card/TicketTracking.vue'),
},
{
path: 'log',
name: 'TicketLog',
meta: {
title: 'log',
icon: 'history',
},
component: () => import('src/pages/Ticket/Card/TicketLog.vue'),
},
{
path: 'observation',
name: 'TicketNotes',
meta: {
title: 'notes',
icon: 'vn:notes',
},
component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
},
{
path: 'picture',
name: 'TicketPicture',
meta: {
title: 'pictures',
icon: 'vn:photo',
},
component: () => import('src/pages/Ticket/Card/TicketPicture.vue'),
},
{
path: 'volume',
name: 'TicketVolume',
meta: {
title: 'volume',
icon: 'vn:volume',
},
component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
},
{
path: 'expedition',
name: 'TicketExpedition',
meta: {
title: 'expedition',
icon: 'vn:package',
},
component: () => import('src/pages/Ticket/Card/TicketExpedition.vue'),
},
{
path: 'service',
name: 'TicketService',
meta: {
title: 'services',
icon: 'vn:services',
},
component: () => import('src/pages/Ticket/Card/TicketService.vue'),
},
{
path: 'package',
name: 'TicketPackage',
meta: {
title: 'packages',
icon: 'vn:bucket',
},
component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
},
{
path: 'components',
name: 'TicketComponents',
meta: {
title: 'components',
icon: 'vn:components',
},
component: () => import('src/pages/Ticket/Card/TicketComponents.vue'),
},
{
path: 'sale-tracking',
name: 'TicketSaleTracking',
meta: {
title: 'saleTracking',
icon: 'assignment',
},
component: () => import('src/pages/Ticket/Card/TicketSaleTracking.vue'),
},
{
path: 'dms',
name: 'TicketDms',
meta: {
title: 'dms',
icon: 'cloud_upload',
},
component: () => import('src/pages/Ticket/Card/TicketDms.vue'),
},
{
path: 'boxing',
name: 'TicketBoxing',
meta: {
title: 'boxing',
icon: 'science',
},
component: () => import('src/pages/Ticket/Card/TicketBoxing.vue'),
},
{
path: 'sms',
name: 'TicketSms',
meta: {
title: 'sms',
icon: 'sms',
},
component: () => import('src/pages/Ticket/Card/TicketSms.vue'),
},
],
};
export default {
name: 'Ticket',
path: '/ticket',
meta: {
title: 'tickets',
icon: 'vn:ticket',
moduleName: 'Ticket',
keyBinding: 't',
menu: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'],
},
component: RouterView,
redirect: { name: 'TicketMain' },
children: [ children: [
{ {
name: 'TicketMain', name: 'TicketMain',
path: '', path: '',
component: () => import('src/components/common/VnSectionMain.vue'), component: () => import('src/components/common/VnModule.vue'),
redirect: { name: 'TicketList' }, redirect: { name: 'TicketIndexMain' },
children: [ children: [
{ {
path: 'list', path: '',
name: 'TicketList', name: 'TicketIndexMain',
meta: { redirect: { name: 'TicketList' },
title: 'list',
icon: 'view_list',
},
component: () => import('src/pages/Ticket/TicketList.vue'), component: () => import('src/pages/Ticket/TicketList.vue'),
children: [
{
name: 'TicketList',
path: 'list',
meta: {
title: 'list',
icon: 'view_list',
},
},
ticketCard,
],
}, },
{ {
path: 'create', path: 'create',
@ -86,170 +258,5 @@ export default {
}, },
], ],
}, },
{
name: 'TicketCard',
path: ':id',
component: () => import('src/pages/Ticket/Card/TicketCard.vue'),
redirect: { name: 'TicketSummary' },
children: [
{
path: 'summary',
name: 'TicketSummary',
meta: {
title: 'summary',
icon: 'launch',
},
component: () => import('src/pages/Ticket/Card/TicketSummary.vue'),
},
{
path: 'basic-data',
name: 'TicketBasicData',
meta: {
title: 'basicData',
icon: 'vn:settings',
},
component: () =>
import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'),
},
{
path: 'sale',
name: 'TicketSale',
meta: {
title: 'sale',
icon: 'vn:lines',
},
component: () => import('src/pages/Ticket/Card/TicketSale.vue'),
},
{
path: 'request',
name: 'TicketPurchaseRequest',
meta: {
title: 'purchaseRequest',
icon: 'vn:buyrequest',
},
component: () =>
import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'),
},
{
path: 'tracking',
name: 'TicketTracking',
meta: {
title: 'tracking',
icon: 'vn:eye',
},
component: () => import('src/pages/Ticket/Card/TicketTracking.vue'),
},
{
path: 'log',
name: 'TicketLog',
meta: {
title: 'log',
icon: 'history',
},
component: () => import('src/pages/Ticket/Card/TicketLog.vue'),
},
{
path: 'observation',
name: 'TicketNotes',
meta: {
title: 'notes',
icon: 'vn:notes',
},
component: () => import('src/pages/Ticket/Card/TicketNotes.vue'),
},
{
path: 'picture',
name: 'TicketPicture',
meta: {
title: 'pictures',
icon: 'vn:photo',
},
component: () => import('src/pages/Ticket/Card/TicketPicture.vue'),
},
{
path: 'volume',
name: 'TicketVolume',
meta: {
title: 'volume',
icon: 'vn:volume',
},
component: () => import('src/pages/Ticket/Card/TicketVolume.vue'),
},
{
path: 'expedition',
name: 'TicketExpedition',
meta: {
title: 'expedition',
icon: 'vn:package',
},
component: () => import('src/pages/Ticket/Card/TicketExpedition.vue'),
},
{
path: 'service',
name: 'TicketService',
meta: {
title: 'services',
icon: 'vn:services',
},
component: () => import('src/pages/Ticket/Card/TicketService.vue'),
},
{
path: 'package',
name: 'TicketPackage',
meta: {
title: 'packages',
icon: 'vn:bucket',
},
component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
},
{
path: 'components',
name: 'TicketComponents',
meta: {
title: 'components',
icon: 'vn:components',
},
component: () => import('src/pages/Ticket/Card/TicketComponents.vue'),
},
{
path: 'sale-tracking',
name: 'TicketSaleTracking',
meta: {
title: 'saleTracking',
icon: 'assignment',
},
component: () =>
import('src/pages/Ticket/Card/TicketSaleTracking.vue'),
},
{
path: 'dms',
name: 'TicketDms',
meta: {
title: 'dms',
icon: 'cloud_upload',
},
component: () => import('src/pages/Ticket/Card/TicketDms.vue'),
},
{
path: 'boxing',
name: 'TicketBoxing',
meta: {
title: 'boxing',
icon: 'science',
},
component: () => import('src/pages/Ticket/Card/TicketBoxing.vue'),
},
{
path: 'sms',
name: 'TicketSms',
meta: {
title: 'sms',
icon: 'sms',
},
component: () => import('src/pages/Ticket/Card/TicketSms.vue'),
},
],
},
], ],
}; };