0
0
Fork 0

Merge pull request 'Ticket tracking' (!485) from hyervoni/salix-front-mindshore:feature/TicketTracking into dev

Reviewed-on: verdnatura/salix-front#485
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
Reviewed-by: Javier Segarra <jsegarra@verdnatura.es>
This commit is contained in:
Javier Segarra 2024-07-01 12:51:10 +00:00
commit 2aabe75343
7 changed files with 237 additions and 8 deletions

View File

@ -450,6 +450,7 @@ ticket:
futureTickets: Future tickets futureTickets: Future tickets
purchaseRequest: Purchase request purchaseRequest: Purchase request
weeklyTickets: Weekly tickets weeklyTickets: Weekly tickets
tracking: Tracking
list: list:
nickname: Nickname nickname: Nickname
state: State state: State

View File

@ -449,6 +449,7 @@ ticket:
futureTickets: Tickets a futuro futureTickets: Tickets a futuro
purchaseRequest: Petición de compra purchaseRequest: Petición de compra
weeklyTickets: Tickets programados weeklyTickets: Tickets programados
tracking: Estados
list: list:
nickname: Alias nickname: Alias
state: Estado state: Estado

View File

@ -0,0 +1,86 @@
<script setup>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import { useState } from 'src/composables/useState';
const emit = defineEmits(['onRequestCreated']);
const route = useRoute();
const { t } = useI18n();
const state = useState();
const user = state.getUser();
const stateFetchDataRef = ref(null);
const statesOptions = ref([]);
const workersOptions = ref([]);
const onStateFkChange = (formData) => (formData.userFk = user.value.id);
</script>
<template>
<FetchData
ref="stateFetchDataRef"
url="States"
auto-load
@on-fetch="(data) => (statesOptions = data)"
/>
<FetchData
url="Workers/search"
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
auto-load
@on-fetch="(data) => (workersOptions = data)"
/>
<FormModelPopup
:title="t('Create tracking')"
url-create="Tickets/state"
model="CreateTicketTracking"
:form-initial-data="{ ticketFk: route.params.id }"
@on-data-saved="() => emit('onRequestCreated')"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnSelect
v-model="data.stateFk"
:label="t('tracking.state')"
:options="statesOptions"
@update:model-value="onStateFkChange(data)"
hide-selected
option-label="name"
option-value="id"
/>
<VnSelect
:label="t('tracking.worker')"
v-model="data.userFk"
:options="workersOptions"
hide-selected
option-label="name"
option-value="id"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel>
{{ opt.name }}
</QItemLabel>
<QItemLabel caption>
{{ opt.nickname }}, {{ opt.code }}
</QItemLabel>
</QItemSection>
</QItem>
</template></VnSelect
>
</VnRow>
</template>
</FormModelPopup>
</template>
<i18n>
es:
Create tracking: Crear estado
</i18n>

View File

@ -0,0 +1,121 @@
<script setup>
import { ref, computed, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import TicketCreateTracking from './TicketCreateTracking.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import { toDateTimeFormat } from 'src/filters/date.js';
const route = useRoute();
const { t } = useI18n();
const createTrackingDialogRef = ref(null);
const paginateRef = ref(null);
watch(
() => route.params.id,
async (val) => {
paginateFilter.where.ticketFk = val;
paginateRef.value.fetch();
}
);
const paginateFilter = reactive({
include: [
{
relation: 'user',
scope: {
fields: ['id', 'name'],
include: {
relation: 'worker',
scope: {
fields: ['id'],
},
},
},
},
{
relation: 'state',
scope: {
fields: ['name'],
},
},
],
order: ['created DESC'],
where: {
ticketFk: route.params.id,
},
});
const columns = computed(() => [
{
label: t('tracking.state'),
name: 'state',
field: 'state',
align: 'left',
format: (val) => val.name,
},
{
label: t('tracking.worker'),
name: 'worker',
align: 'left',
},
{
label: t('tracking.created'),
name: 'created',
field: 'created',
align: 'left',
format: (val) => toDateTimeFormat(val),
},
]);
const openCreateModal = () => createTrackingDialogRef.value.show();
</script>
<template>
<QPage class="column items-center q-pa-md">
<VnPaginate
ref="paginateRef"
data-key="TicketTracking"
:filter="paginateFilter"
url="TicketTrackings"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
>
<template #body-cell-worker="{ row }">
<QTd @click.stop>
<QBtn flat color="primary">
{{ row.user?.name }}
<WorkerDescriptorProxy :id="row.user?.worker?.id" />
</QBtn>
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
<QDialog
ref="createTrackingDialogRef"
transition-show="scale"
transition-hide="scale"
>
<TicketCreateTracking @on-request-created="paginateRef.fetch()" />
</QDialog>
<QPageSticky :offset="[20, 20]">
<QBtn @click="openCreateModal()" color="primary" fab icon="add" />
<QTooltip class="text-no-wrap">
{{ t('tracking.addState') }}
</QTooltip>
</QPageSticky>
</QPage>
</template>

View File

@ -149,3 +149,8 @@ weeklyTickets:
salesperson: Salesperson salesperson: Salesperson
search: Search weekly tickets search: Search weekly tickets
searchInfo: Search weekly tickets by id or client id searchInfo: Search weekly tickets by id or client id
tracking:
state: State
worker: Worker
created: Created
addState: Add state

View File

@ -1,3 +1,8 @@
tracking:
state: Estado
worker: Trabajador
created: Fecha creación
addState: Añadir estado
card: card:
search: Buscar tickets search: Buscar tickets
searchInfo: Buscar tickets por identificador o alias searchInfo: Buscar tickets por identificador o alias

View File

@ -19,6 +19,7 @@ export default {
'TicketSale', 'TicketSale',
'TicketLog', 'TicketLog',
'TicketPurchaseRequest', 'TicketPurchaseRequest',
'TicketTracking',
'TicketVolume', 'TicketVolume',
'TicketNotes', 'TicketNotes',
], ],
@ -31,8 +32,8 @@ export default {
redirect: { name: 'TicketList' }, redirect: { name: 'TicketList' },
children: [ children: [
{ {
name: 'TicketList',
path: 'list', path: 'list',
name: 'TicketList',
meta: { meta: {
title: 'list', title: 'list',
icon: 'view_list', icon: 'view_list',
@ -40,8 +41,8 @@ export default {
component: () => import('src/pages/Ticket/TicketList.vue'), component: () => import('src/pages/Ticket/TicketList.vue'),
}, },
{ {
name: 'TicketCreate',
path: 'create', path: 'create',
name: 'TicketCreate',
meta: { meta: {
title: 'createTicket', title: 'createTicket',
icon: 'vn:ticketAdd', icon: 'vn:ticketAdd',
@ -50,8 +51,8 @@ export default {
component: () => import('src/pages/Ticket/TicketCreate.vue'), component: () => import('src/pages/Ticket/TicketCreate.vue'),
}, },
{ {
name: 'TicketWeekly',
path: 'weekly', path: 'weekly',
name: 'TicketWeekly',
meta: { meta: {
title: 'weeklyTickets', title: 'weeklyTickets',
icon: 'access_time', icon: 'access_time',
@ -59,8 +60,8 @@ export default {
component: () => import('src/pages/Ticket/TicketWeekly.vue'), component: () => import('src/pages/Ticket/TicketWeekly.vue'),
}, },
{ {
name: 'TicketFuture',
path: 'future', path: 'future',
name: 'TicketFuture',
meta: { meta: {
title: 'futureTickets', title: 'futureTickets',
icon: 'keyboard_double_arrow_right', icon: 'keyboard_double_arrow_right',
@ -68,8 +69,8 @@ export default {
component: () => import('src/pages/Ticket/TicketFuture.vue'), component: () => import('src/pages/Ticket/TicketFuture.vue'),
}, },
{ {
name: 'TicketAdvance',
path: 'advance', path: 'advance',
name: 'TicketAdvance',
meta: { meta: {
title: 'ticketAdvance', title: 'ticketAdvance',
icon: 'keyboard_double_arrow_left', icon: 'keyboard_double_arrow_left',
@ -85,8 +86,8 @@ export default {
redirect: { name: 'TicketSummary' }, redirect: { name: 'TicketSummary' },
children: [ children: [
{ {
name: 'TicketSummary',
path: 'summary', path: 'summary',
name: 'TicketSummary',
meta: { meta: {
title: 'summary', title: 'summary',
icon: 'launch', icon: 'launch',
@ -94,8 +95,8 @@ export default {
component: () => import('src/pages/Ticket/Card/TicketSummary.vue'), component: () => import('src/pages/Ticket/Card/TicketSummary.vue'),
}, },
{ {
name: 'TicketBasicData',
path: 'basic-data', path: 'basic-data',
name: 'TicketBasicData',
meta: { meta: {
title: 'basicData', title: 'basicData',
icon: 'vn:settings', icon: 'vn:settings',
@ -104,8 +105,8 @@ export default {
import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'), import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'),
}, },
{ {
name: 'TicketSale',
path: 'sale', path: 'sale',
name: 'TicketSale',
meta: { meta: {
title: 'sale', title: 'sale',
icon: 'vn:lines', icon: 'vn:lines',
@ -122,6 +123,15 @@ export default {
component: () => component: () =>
import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'), 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', path: 'log',
name: 'TicketLog', name: 'TicketLog',