0
0
Fork 0

Create route summary

This commit is contained in:
Kevin Martinez 2024-01-18 08:45:41 -04:00
parent 8f47a0bc2b
commit 661e9d4368
11 changed files with 806 additions and 219 deletions

View File

@ -809,7 +809,8 @@ export default {
cmrsList: 'External CMRs list', cmrsList: 'External CMRs list',
RouteList: 'List', RouteList: 'List',
create: 'Create', create: 'Create',
basicData: 'Basic Data' basicData: 'Basic Data',
summary: 'Summary'
}, },
cmr: { cmr: {
list: { list: {

View File

@ -809,6 +809,7 @@ export default {
RouteList: 'Listado', RouteList: 'Listado',
create: 'Crear', create: 'Crear',
basicData: 'Datos básicos', basicData: 'Datos básicos',
summary: 'Summary',
}, },
cmr: { cmr: {
list: { list: {

View File

@ -1,6 +1,7 @@
<script setup> <script setup>
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import LeftMenu from 'components/LeftMenu.vue'; import LeftMenu from 'components/LeftMenu.vue';
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
// import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue'; // import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -8,7 +9,7 @@ const stateStore = useStateStore();
<template> <template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit"> <QScrollArea class="fit">
<!-- <ShelvingDescriptor />--> <RouteDescriptor />
<QSeparator /> <QSeparator />
<LeftMenu source="card" /> <LeftMenu source="card" />
</QScrollArea> </QScrollArea>

View File

@ -0,0 +1,104 @@
<script setup>
import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'components/ui/VnLv.vue';
import useCardDescription from 'composables/useCardDescription';
import { dashIfEmpty, toDate } from 'src/filters';
import RouteDescriptorMenu from 'pages/Route/Card/RouteDescriptorMenu.vue';
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => {
return $props.id || route.params.id;
});
const filter = {
fields: [
'id',
'workerFk',
'agencyModeFk',
'created',
'm3',
'warehouseFk',
'description',
'vehicleFk',
'kmStart',
'kmEnd',
'started',
'finished',
'cost',
'zoneFk',
'isOk',
],
include: [
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
{
relation: 'vehicle',
scope: { fields: ['id', 'm3'] },
},
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['id'],
include: { relation: 'emailUser', scope: { fields: ['email'] } },
},
},
},
},
],
};
const data = ref(useCardDescription());
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
</script>
<template>
<CardDescriptor
module="Route"
:url="`Routes/${entityId}`"
:filter="filter"
:title="data.title"
:subtitle="data.subtitle"
data-key="Routes"
@on-fetch="setData"
>
<template #body="{ entity }">
<VnLv :label="t('Date')" :value="toDate(entity?.created)" />
<VnLv :label="t('Agency')" :value="entity?.agencyMode?.name" />
<VnLv :label="t('Zone')" :value="entity?.zone?.name" />
<VnLv
:label="t('Volume')"
:value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty(
entity?.vehicle?.m3
)} `"
/>
<VnLv :label="t('Description')" :value="entity?.description" />
</template>
<template #menu="{ entity }">
<RouteDescriptorMenu :route="entity" />
</template>
</CardDescriptor>
</template>
<i18n>
es:
Date: Fecha
Agency: Agencia
Zone: Zona
Volume: Volumen
Description: Descripción
</i18n>

View File

@ -0,0 +1,63 @@
<script setup>
import axios from 'axios';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnConfirm from 'components/ui/VnConfirm.vue';
const props = defineProps({
route: {
type: Object,
required: true,
},
});
const router = useRouter();
const quasar = useQuasar();
const { t } = useI18n();
function confirmRemove() {
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t('confirmDeletion'),
message: t('confirmDeletionMessage'),
promise: remove,
},
})
.onOk(async () => await router.push({ name: 'RouteList' }));
}
async function remove() {
if (!props.route.id) {
return;
}
await axios.delete(`Routes/${props.route.id}`);
quasar.notify({
message: t('globals.dataDeleted'),
type: 'positive',
});
}
// TODO: Add reports
</script>
<template>
<QItem @click="confirmRemove" v-ripple clickable>
<QItemSection avatar>
<QIcon name="delete" />
</QItemSection>
<QItemSection>{{ t('deleteRoute') }}</QItemSection>
</QItem>
</template>
<i18n>
en:
confirmDeletion: Confirm deletion
confirmDeletionMessage: Are you sure you want to delete this route?
deleteRoute: Delete route
es:
confirmDeletion: Confirmar eliminación,
confirmDeletionMessage: Seguro que quieres eliminar esta ruta?
deleteRoute: Eliminar ruta
</i18n>

View File

@ -0,0 +1,15 @@
<script setup>
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
},
});
</script>
<template>
<QPopupProxy>
<RouteDescriptor v-if="$props.id" :id="$props.id" />
</QPopupProxy>
</template>

View File

@ -72,8 +72,7 @@ const routeFilter = {
const onSave = (data, response) => { const onSave = (data, response) => {
if (isNew) { if (isNew) {
axios.post(`Routes/${response.data?.id}/updateWorkCenter`); axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
// TODO: Add summary router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
// router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
} }
}; };
</script> </script>

View File

@ -0,0 +1,314 @@
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'components/ui/VnLv.vue';
import { QIcon } from 'quasar';
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
import axios from 'axios';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
const $props = defineProps({
id: {
type: Number,
default: 0,
},
});
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
const entityId = computed(() => $props.id || route.params.id);
const isDialog = Boolean($props.id);
const hideRightDrawer = () => {
if (!isDialog) {
stateStore.rightDrawer = false;
}
};
onMounted(hideRightDrawer);
onUnmounted(hideRightDrawer);
const getTotalPackages = (tickets) => {
return (tickets || []).reduce((sum, ticket) => sum + ticket.packages, 0);
};
const ticketColumns = ref([
{
name: 'order',
label: t('route.summary.order'),
field: (row) => dashIfEmpty(row?.priority),
sortable: false,
align: 'center',
},
{
name: 'street',
label: t('route.summary.street'),
field: (row) => row?.street,
sortable: false,
align: 'left',
},
{
name: 'city',
label: t('route.summary.city'),
field: (row) => row?.city,
sortable: false,
align: 'left',
},
{
name: 'pc',
label: t('route.summary.pc'),
field: (row) => row?.postalCode,
sortable: false,
align: 'center',
},
{
name: 'client',
label: t('route.summary.client'),
field: (row) => row?.nickname,
sortable: false,
align: 'left',
},
{
name: 'warehouse',
label: t('route.summary.warehouse'),
field: (row) => row?.warehouseName,
sortable: false,
align: 'left',
},
{
name: 'packages',
label: t('route.summary.packages'),
field: (row) => row?.packages,
sortable: false,
align: 'center',
},
{
name: 'volume',
label: t('route.summary.m3'),
field: (row) => row?.volume,
sortable: false,
align: 'center',
},
{
name: 'packaging',
label: t('route.summary.packaging'),
field: (row) => row?.ipt,
sortable: false,
align: 'center',
},
{
name: 'ticket',
label: t('route.summary.ticket'),
field: (row) => row?.id,
sortable: false,
align: 'right',
},
{
name: 'observations',
label: '',
field: (row) => row?.ticketObservation,
sortable: false,
align: 'left',
},
]);
const openBuscaman = async (route, ticket) => {
if (!route.vehicleFk) throw new Error(`The route doesn't have a vehicle`);
const response = await axios.get(`Routes/${route.vehicleFk}/getDeliveryPoint`);
if (!response.data)
throw new Error(`The route's vehicle doesn't have a delivery point`);
const address = `${response.data}+to:${ticket.postalCode} ${ticket.city} ${ticket.street}`;
window.open(
'https://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=' +
encodeURI(address),
'_blank'
);
};
</script>
<template>
<div class="q-pa-md">
<CardSummary ref="summary" :url="`Routes/${entityId}/summary`">
<template #header-left>
<RouterLink :to="{ name: `RouteSummary`, params: { id: entityId } }">
<QIcon name="open_in_new" color="white" size="sm" />
</RouterLink>
</template>
<template #header="{ entity }">
<span>{{ `${entity?.route.id} - ${entity?.route?.description}` }}</span>
</template>
<template #body="{ entity }">
<QCard class="vn-one">
<VnLv :label="t('ID')" :value="entity?.route.id" />
<VnLv
:label="t('route.summary.date')"
:value="toDate(entity?.route.created)"
/>
<VnLv
:label="t('route.summary.agency')"
:value="entity?.route?.agencyMode?.name"
/>
<VnLv
:label="t('route.summary.vehicle')"
:value="entity.route?.vehicle?.numberPlate"
/>
<VnLv :label="t('route.summary.driver')">
<template #value>
<span class="link">
{{ dashIfEmpty(entity?.route?.worker?.user?.name) }}
<WorkerDescriptorProxy :id="entity.route?.workerFk" />
</span>
</template>
</VnLv>
<VnLv
:label="t('route.summary.cost')"
:value="toCurrency(entity.route?.cost)"
/>
</QCard>
<QCard class="vn-one">
<VnLv
:label="t('route.summary.started')"
:value="toHour(entity?.route.started)"
/>
<VnLv
:label="t('route.summary.finished')"
:value="toHour(entity?.route.finished)"
/>
<VnLv
:label="t('route.summary.kmStart')"
:value="dashIfEmpty(entity?.route?.kmStart)"
/>
<VnLv
:label="t('route.summary.kmEnd')"
:value="dashIfEmpty(entity?.route?.kmEnd)"
/>
<VnLv
:label="t('route.summary.volume')"
:value="`${dashIfEmpty(entity?.route?.m3)} / ${dashIfEmpty(
entity?.route?.vehicle?.m3
)} `"
/>
<VnLv
:label="t('route.summary.packages')"
:value="getTotalPackages(entity.tickets)"
/>
</QCard>
<QCard class="vn-one">
<div class="header">
{{ t('route.summary.description') }}
</div>
<p>
{{ dashIfEmpty(entity?.route?.description) }}
</p>
</QCard>
<QCard class="vn-max">
<div class="header">
{{ t('route.summary.tickets') }}
</div>
<QTable
:columns="ticketColumns"
:rows="entity?.tickets"
:rows-per-page-options="[0]"
row-key="id"
flat
hide-pagination
>
<template #body-cell-city="{ value, row }">
<QTd auto-width>
<span
class="text-primary cursor-pointer"
@click="openBuscaman(entity?.route, row)"
>
{{ value }}
</span>
</QTd>
</template>
<template #body-cell-client="{ value, row }">
<QTd auto-width>
<span class="text-primary cursor-pointer">
{{ value }}
<CustomerDescriptorProxy :id="row?.clientFk" />
</span>
</QTd>
</template>
<template #body-cell-ticket="{ value, row }">
<QTd auto-width>
<span class="text-primary cursor-pointer">
{{ value }}
<TicketDescriptorProxy :id="row?.id" />
</span>
</QTd>
</template>
<template #body-cell-observations="{ value }">
<QTd auto-width>
<QIcon
v-if="value"
name="vn:notes"
color="primary"
class="cursor-pointer"
>
<QTooltip>{{ value }}</QTooltip>
</QIcon>
</QTd>
</template>
</QTable>
</QCard>
</template>
</CardSummary>
</div>
</template>
<i18n>
en:
route:
summary:
date: Date
agency: Agency
vehicle: Vehicle
driver: Driver
cost: Cost
started: Started time
finished: Finished time
kmStart: Km start
kmEnd: Km end
volume: Volume
packages: Packages
description: Description
tickets: Tickets
order: Order
street: Street
city: City
pc: PC
client: Client
warehouse: Warehouse
m3:
packaging: Packaging
ticket: Ticket
es:
route:
summary:
date: Fecha
agency: Agencia
vehicle: Vehículo
driver: Conductor
cost: Costo
started: Hora inicio
finished: Hora fin
kmStart: Km inicio
kmEnd: Km fin
volume: Volumen
packages: Bultos
description: Descripción
tickets: Tickets
order: Orden
street: Dirección fiscal
city: Población
pc: CP
client: Cliente
warehouse: Almacén
packaging: Encajado
</i18n>

View File

@ -0,0 +1,29 @@
<script setup>
import { useDialogPluginComponent } from 'quasar';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
},
});
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script>
<template>
<QDialog ref="dialogRef" @hide="onDialogHide">
<RouteSummary v-if="$props.id" :id="$props.id" />
</QDialog>
</template>
<style lang="scss">
.q-dialog .route .header {
position: sticky;
z-index: $z-max;
top: 0;
}
</style>

View File

@ -13,12 +13,13 @@ import VnInputTime from 'components/common/VnInputTime.vue';
import axios from 'axios'; import axios from 'axios';
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue'; import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
import RouteFilter from 'pages/Route/Card/RouteFilter.vue'; import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
import { useQuasar } from 'quasar';
import RouteSummaryDialog from 'pages/Route/Card/RouteSummaryDialog.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
// const router = useRouter();
// const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const { validate } = useValidator(); const { validate } = useValidator();
const quasar = useQuasar();
const to = Date.vnNew(); const to = Date.vnNew();
to.setDate(to.getDate() + 1); to.setDate(to.getDate() + 1);
@ -98,6 +99,12 @@ const columns = computed(() => [
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{
name: 'actions',
label: '',
sortable: false,
align: 'right',
},
]); ]);
const refreshKey = ref(0); const refreshKey = ref(0);
@ -151,6 +158,18 @@ const markAsServed = () => {
refreshKey.value++; refreshKey.value++;
startingDate.value = null; startingDate.value = null;
}; };
function previewRoute(id) {
if (!id) {
return;
}
quasar.dialog({
component: RouteSummaryDialog,
componentProps: {
id,
},
});
}
</script> </script>
<template> <template>
@ -187,7 +206,7 @@ const markAsServed = () => {
autofocus autofocus
/> />
</QCardSection> </QCardSection>
<!-- TODO: Add report -->
<QCardActions align="right"> <QCardActions align="right">
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" /> <QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
<QBtn color="primary" v-close-popup @click="cloneRoutes"> <QBtn color="primary" v-close-popup @click="cloneRoutes">
@ -232,213 +251,237 @@ const markAsServed = () => {
</QBtn> </QBtn>
</div> </div>
</QToolbar> </QToolbar>
<VnPaginate <div class="route-list">
:key="refreshKey" <VnPaginate
data-key="RouteList" :key="refreshKey"
url="Routes/filter" data-key="RouteList"
:order="['created DESC', 'id DESC']" url="Routes/filter"
:limit="20" :order="['created DESC', 'id DESC']"
auto-load :limit="20"
> auto-load
<template #body="{ rows }"> >
<div class="q-pa-md"> <template #body="{ rows }">
<QTable <div class="q-pa-md">
v-model:selected="selectedRows" <QTable
:columns="columns" v-model:selected="selectedRows"
:rows="rows" :columns="columns"
flat :rows="rows"
row-key="id" flat
selection="multiple" row-key="id"
:rows-per-page-options="[0]" selection="multiple"
> :rows-per-page-options="[0]"
<template #body-cell-worker="props"> hide-pagination
<QTd :props="props"> >
{{ props.row?.workerUserName }} <template #body-cell-worker="props">
<QPopupEdit <QTd :props="props">
:model-value="props.row.workerFk" {{ props.row?.workerUserName }}
v-slot="scope" <QPopupEdit
buttons :model-value="props.row.workerFk"
@update:model-value=" v-slot="scope"
(worker) => updateWorker(props.row, worker) buttons
" @update:model-value="
> (worker) => updateWorker(props.row, worker)
<VnSelectFilter "
:label="t('Worker')"
v-model="scope.value"
:options="workers"
option-value="id"
option-label="name"
hide-selected
autofocus
:emit-value="false"
:rules="validate('Route.workerFk')"
:is-clearable="false"
@keyup.enter="scope.set"
@focus="($event) => $event.target.select()"
> >
<template #option="{ opt, itemProps }"> <VnSelectFilter
<QItem :label="t('Worker')"
v-bind="itemProps" v-model="scope.value"
class="q-pa-xs row items-center" :options="workers"
> option-value="id"
<QItemSection option-label="name"
class="col-9 justify-center" hide-selected
autofocus
:emit-value="false"
:rules="validate('Route.workerFk')"
:is-clearable="false"
@keyup.enter="scope.set"
@focus="($event) => $event.target.select()"
>
<template #option="{ opt, itemProps }">
<QItem
v-bind="itemProps"
class="q-pa-xs row items-center"
> >
<span>{{ opt.name }}</span> <QItemSection
<span class="text-grey">{{ class="col-9 justify-center"
opt.nickname >
}}</span> <span>{{ opt.name }}</span>
</QItemSection> <span class="text-grey">{{
</QItem> opt.nickname
</template> }}</span>
</VnSelectFilter> </QItemSection>
</QPopupEdit> </QItem>
</QTd> </template>
</template> </VnSelectFilter>
<template #body-cell-agency="props"> </QPopupEdit>
<QTd :props="props"> </QTd>
{{ props.row?.agencyName }} </template>
<QPopupEdit <template #body-cell-agency="props">
:model-value="props.row.agencyModeFk" <QTd :props="props">
v-slot="scope" {{ props.row?.agencyName }}
buttons <QPopupEdit
@update:model-value=" :model-value="props.row.agencyModeFk"
(agency) => updateAgency(props.row, agency) v-slot="scope"
" buttons
> @update:model-value="
<VnSelectFilter (agency) => updateAgency(props.row, agency)
:label="t('Agency')" "
v-model="scope.value" >
:options="agencyList" <VnSelectFilter
option-value="id" :label="t('Agency')"
option-label="name" v-model="scope.value"
hide-selected :options="agencyList"
autofocus option-value="id"
:emit-value="false" option-label="name"
:rules="validate('route.agencyFk')" hide-selected
:is-clearable="false" autofocus
@keyup.enter="scope.set" :emit-value="false"
@focus="($event) => $event.target.select()" :rules="validate('route.agencyFk')"
/> :is-clearable="false"
</QPopupEdit> @keyup.enter="scope.set"
</QTd> @focus="($event) => $event.target.select()"
</template> />
<template #body-cell-vehicle="props"> </QPopupEdit>
<QTd :props="props"> </QTd>
{{ props.row?.vehiclePlateNumber }} </template>
<QPopupEdit <template #body-cell-vehicle="props">
:model-value="props.row.vehicleFk" <QTd :props="props">
v-slot="scope" {{ props.row?.vehiclePlateNumber }}
buttons <QPopupEdit
@update:model-value=" :model-value="props.row.vehicleFk"
(vehicle) => updateVehicle(props.row, vehicle) v-slot="scope"
" buttons
> @update:model-value="
<VnSelectFilter (vehicle) => updateVehicle(props.row, vehicle)
:label="t('Vehicle')" "
v-model="scope.value" >
:options="vehicleList" <VnSelectFilter
option-value="id" :label="t('Vehicle')"
option-label="numberPlate" v-model="scope.value"
hide-selected :options="vehicleList"
autofocus option-value="id"
:emit-value="false" option-label="numberPlate"
:rules="validate('route.vehicleFk')" hide-selected
:is-clearable="false" autofocus
@keyup.enter="scope.set" :emit-value="false"
@focus="($event) => $event.target.select()" :rules="validate('route.vehicleFk')"
/> :is-clearable="false"
</QPopupEdit> @keyup.enter="scope.set"
</QTd> @focus="($event) => $event.target.select()"
</template> />
<template #body-cell-date="props"> </QPopupEdit>
<QTd :props="props"> </QTd>
{{ toDate(props.row?.created) }} </template>
<QPopupEdit <template #body-cell-date="props">
v-model="props.row.created" <QTd :props="props">
v-slot="scope" {{ toDate(props.row?.created) }}
@update:model-value="updateRoute(props.row)" <QPopupEdit
buttons v-model="props.row.created"
> v-slot="scope"
<VnInputDate @update:model-value="updateRoute(props.row)"
v-model="scope.value" buttons
autofocus >
:label="t('Date')" <VnInputDate
:rules="validate('route.created')" v-model="scope.value"
:is-clearable="false" autofocus
@keyup.enter="scope.set" :label="t('Date')"
@focus="($event) => $event.target.select()" :rules="validate('route.created')"
/> :is-clearable="false"
</QPopupEdit> @keyup.enter="scope.set"
</QTd> @focus="($event) => $event.target.select()"
</template> />
<template #body-cell-description="props"> </QPopupEdit>
<QTd :props="props"> </QTd>
{{ props.row?.description }} </template>
<QPopupEdit <template #body-cell-description="props">
v-model="props.row.description" <QTd :props="props">
v-slot="scope" {{ props.row?.description }}
@update:model-value="updateRoute(props.row)" <QPopupEdit
buttons v-model="props.row.description"
> v-slot="scope"
<VnInput @update:model-value="updateRoute(props.row)"
v-model="scope.value" buttons
autofocus >
:label="t('Description')" <VnInput
:rules="validate('route.description')" v-model="scope.value"
:is-clearable="false" autofocus
@keyup.enter="scope.set" :label="t('Description')"
@focus="($event) => $event.target.select()" :rules="validate('route.description')"
/> :is-clearable="false"
</QPopupEdit> @keyup.enter="scope.set"
</QTd> @focus="($event) => $event.target.select()"
</template> />
<template #body-cell-started="props"> </QPopupEdit>
<QTd :props="props"> </QTd>
{{ toHour(props.row.started) }} </template>
<QPopupEdit <template #body-cell-started="props">
v-model="props.row.started" <QTd :props="props">
v-slot="scope" {{ toHour(props.row.started) }}
buttons <QPopupEdit
@update:model-value="updateRoute(props.row)" v-model="props.row.started"
> v-slot="scope"
<VnInputTime buttons
v-model="scope.value" @update:model-value="updateRoute(props.row)"
autofocus >
:label="t('Hour started')" <VnInputTime
:rules="validate('route.started')" v-model="scope.value"
:is-clearable="false" autofocus
@keyup.enter="scope.set" :label="t('Hour started')"
@focus="($event) => $event.target.select()" :rules="validate('route.started')"
/> :is-clearable="false"
</QPopupEdit> @keyup.enter="scope.set"
</QTd> @focus="($event) => $event.target.select()"
</template> />
<template #body-cell-finished="props"> </QPopupEdit>
<QTd :props="props"> </QTd>
{{ toHour(props.row.finished) }} </template>
<QPopupEdit <template #body-cell-finished="props">
v-model="props.row.finished" <QTd :props="props">
v-slot="scope" {{ toHour(props.row.finished) }}
buttons <QPopupEdit
@update:model-value="updateRoute(props.row)" v-model="props.row.finished"
> v-slot="scope"
<VnInputTime buttons
v-model="scope.value" @update:model-value="updateRoute(props.row)"
autofocus >
:label="t('Hour finished')" <VnInputTime
:rules="validate('route.finished')" v-model="scope.value"
:is-clearable="false" autofocus
@keyup.enter="scope.set" :label="t('Hour finished')"
@focus="($event) => $event.target.select()" :rules="validate('route.finished')"
/> :is-clearable="false"
</QPopupEdit> @keyup.enter="scope.set"
</QTd> @focus="($event) => $event.target.select()"
</template> />
</QTable> </QPopupEdit>
</div> </QTd>
</template> </template>
</VnPaginate> <template #body-cell-actions="props">
<QTd :props="props">
<div class="table-actions">
<QIcon
name="vn:ticketAdd"
size="xs"
color="primary"
>
<QTooltip>{{ t('Add ticket') }}</QTooltip>
</QIcon>
<QIcon
name="preview"
size="xs"
color="primary"
@click="previewRoute(props?.row?.id)"
>
<QTooltip>{{ t('Preview') }}</QTooltip>
</QIcon>
</div>
</QTd>
</template>
</QTable>
</div>
</template>
</VnPaginate>
</div>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<RouterLink :to="{ name: 'RouteCreate' }"> <RouterLink :to="{ name: 'RouteCreate' }">
<QBtn fab icon="add" color="primary" /> <QBtn fab icon="add" color="primary" />
@ -451,9 +494,18 @@ const markAsServed = () => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.card-list { .route-list {
width: 100%; width: 100%;
max-width: 60em; }
.table-actions {
display: flex;
align-items: center;
gap: 12px;
i {
cursor: pointer;
}
} }
</style> </style>
<i18n> <i18n>

View File

@ -11,14 +11,14 @@ export default {
redirect: { name: 'RouteMain' }, redirect: { name: 'RouteMain' },
menus: { menus: {
main: ['RouteList', 'CmrList'], main: ['RouteList', 'CmrList'],
card: [], card: ['RouteBasicData'],
}, },
children: [ children: [
{ {
path: '/route', path: '/route',
name: 'RouteMain', name: 'RouteMain',
component: () => import('src/pages/Route/RouteMain.vue'), component: () => import('src/pages/Route/RouteMain.vue'),
redirect: { name: 'CmrList' }, redirect: { name: 'RouteList' },
children: [ children: [
{ {
path: 'cmr', path: 'cmr',
@ -34,7 +34,7 @@ export default {
name: 'RouteList', name: 'RouteList',
meta: { meta: {
title: 'RouteList', title: 'RouteList',
icon: 'vn:delivery', icon: 'view_list',
}, },
component: () => import('src/pages/Route/RouteList.vue'), component: () => import('src/pages/Route/RouteList.vue'),
}, },
@ -52,8 +52,7 @@ export default {
name: 'RouteCard', name: 'RouteCard',
path: ':id', path: ':id',
component: () => import('src/pages/Route/Card/RouteCard.vue'), component: () => import('src/pages/Route/Card/RouteCard.vue'),
// TODO: Add summary redirect: { name: 'RouteSummary' },
redirect: { name: 'RouteBasicData' },
children: [ children: [
{ {
name: 'RouteBasicData', name: 'RouteBasicData',
@ -64,6 +63,15 @@ export default {
}, },
component: () => import('pages/Route/Card/RouteForm.vue'), component: () => import('pages/Route/Card/RouteForm.vue'),
}, },
{
name: 'RouteSummary',
path: 'summary',
meta: {
title: 'summary',
icon: 'open_in_new',
},
component: () => import('pages/Route/Card/RouteSummary.vue'),
},
], ],
}, },
], ],