531 lines
21 KiB
Vue
531 lines
21 KiB
Vue
<script setup>
|
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
|
import { useStateStore } from 'stores/useStateStore';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
|
import FetchData from 'components/FetchData.vue';
|
|
import { useValidator } from 'composables/useValidator';
|
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
|
import VnInput from 'components/common/VnInput.vue';
|
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
|
import axios from 'axios';
|
|
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
|
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
|
import { useQuasar } from 'quasar';
|
|
import RouteSummaryDialog from 'pages/Route/Card/RouteSummaryDialog.vue';
|
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
|
|
|
const stateStore = useStateStore();
|
|
const { t } = useI18n();
|
|
const { validate } = useValidator();
|
|
const quasar = useQuasar();
|
|
|
|
const to = Date.vnNew();
|
|
to.setDate(to.getDate() + 1);
|
|
to.setHours(0, 0, 0, 0);
|
|
|
|
const from = Date.vnNew();
|
|
from.setDate(from.getDate());
|
|
from.setHours(0, 0, 0, 0);
|
|
|
|
const params = ref({ from, to });
|
|
|
|
onMounted(() => (stateStore.rightDrawer = true));
|
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
|
|
|
const selectedRows = ref([]);
|
|
const columns = computed(() => [
|
|
{
|
|
name: 'ID',
|
|
label: t('ID'),
|
|
field: (row) => row.id,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'worker',
|
|
label: t('Worker'),
|
|
field: (row) => row.workerUserName,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'agency',
|
|
label: t('Agency'),
|
|
field: (row) => row.agencyName,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'vehicle',
|
|
label: t('Vehicle'),
|
|
field: (row) => row.vehiclePlateNumber,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'date',
|
|
label: t('Date'),
|
|
field: (row) => row.created,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'volume',
|
|
label: 'm³',
|
|
field: (row) => dashIfEmpty(row.m3),
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'description',
|
|
label: t('Description'),
|
|
field: (row) => row.description,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'started',
|
|
label: t('Hour started'),
|
|
field: (row) => toHour(row.started),
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'finished',
|
|
label: t('Hour finished'),
|
|
field: (row) => toHour(row.finished),
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'actions',
|
|
label: '',
|
|
sortable: false,
|
|
align: 'right',
|
|
},
|
|
]);
|
|
|
|
const refreshKey = ref(0);
|
|
const workers = ref([]);
|
|
const agencyList = ref([]);
|
|
const vehicleList = ref([]);
|
|
const updateRoute = async (route) => {
|
|
try {
|
|
return await axios.patch(`Routes/${route.id}`, route);
|
|
} catch (err) {
|
|
return err;
|
|
}
|
|
};
|
|
|
|
const updateVehicle = (row, vehicle) => {
|
|
row.vehicleFk = vehicle.id;
|
|
row.vehiclePlateNumber = vehicle.numberPlate;
|
|
updateRoute(row);
|
|
};
|
|
|
|
const updateAgency = (row, agency) => {
|
|
row.agencyModeFk = agency.id;
|
|
row.agencyName = agency.name;
|
|
updateRoute(row);
|
|
};
|
|
|
|
const updateWorker = (row, worker) => {
|
|
row.workerFk = worker.id;
|
|
row.workerUserName = worker.name;
|
|
updateRoute(row);
|
|
};
|
|
|
|
const confirmationDialog = ref(false);
|
|
const startingDate = ref(null);
|
|
|
|
const cloneRoutes = () => {
|
|
axios.post('Routes/clone', {
|
|
created: startingDate.value,
|
|
ids: selectedRows.value.map((row) => row?.id),
|
|
});
|
|
refreshKey.value++;
|
|
startingDate.value = null;
|
|
};
|
|
|
|
const markAsServed = () => {
|
|
selectedRows.value.forEach((row) => {
|
|
if (row?.id) {
|
|
axios.patch(`Routes/${row?.id}`, { isOk: true });
|
|
}
|
|
});
|
|
refreshKey.value++;
|
|
startingDate.value = null;
|
|
};
|
|
|
|
function previewRoute(id) {
|
|
if (!id) {
|
|
return;
|
|
}
|
|
quasar.dialog({
|
|
component: RouteSummaryDialog,
|
|
componentProps: {
|
|
id,
|
|
},
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<template v-if="stateStore.isHeaderMounted()">
|
|
<Teleport to="#searchbar">
|
|
<RouteSearchbar />
|
|
</Teleport>
|
|
<Teleport to="#actions-append">
|
|
<div class="row q-gutter-x-sm">
|
|
<QBtn
|
|
flat
|
|
@click="stateStore.toggleRightDrawer()"
|
|
round
|
|
dense
|
|
icon="menu"
|
|
>
|
|
<QTooltip bottom anchor="bottom right">
|
|
{{ t('globals.collapseMenu') }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
</div>
|
|
</Teleport>
|
|
</template>
|
|
<QDialog v-model="confirmationDialog">
|
|
<QCard style="min-width: 350px">
|
|
<QCardSection>
|
|
<p class="text-h6 q-ma-none">{{ t('Select the starting date') }}</p>
|
|
</QCardSection>
|
|
|
|
<QCardSection class="q-pt-none">
|
|
<VnInputDate
|
|
:label="t('Stating date')"
|
|
v-model="startingDate"
|
|
autofocus
|
|
/>
|
|
</QCardSection>
|
|
<!-- TODO: Add report -->
|
|
<QCardActions align="right">
|
|
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
|
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
|
{{ t('Clone') }}
|
|
</QBtn>
|
|
</QCardActions>
|
|
</QCard>
|
|
</QDialog>
|
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
|
<QScrollArea class="fit text-grey-8">
|
|
<RouteFilter data-key="RouteList" />
|
|
</QScrollArea>
|
|
</QDrawer>
|
|
<FetchData
|
|
url="Workers/activeWithInheritedRole"
|
|
@on-fetch="(data) => (workers = data)"
|
|
auto-load
|
|
/>
|
|
<FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
|
|
<FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
|
|
<QPage class="column items-center">
|
|
<VnSubToolbar class="bg-vn-dark justify-end">
|
|
<template #st-actions>
|
|
<QBtn
|
|
icon="vn:clone"
|
|
color="primary"
|
|
class="q-mr-sm"
|
|
:disable="!selectedRows?.length"
|
|
@click="confirmationDialog = true"
|
|
>
|
|
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
|
|
</QBtn>
|
|
|
|
<QBtn
|
|
icon="check"
|
|
color="primary"
|
|
class="q-mr-sm"
|
|
:disable="!selectedRows?.length"
|
|
@click="markAsServed"
|
|
>
|
|
<QTooltip>{{ t('Mark as served') }}</QTooltip>
|
|
</QBtn>
|
|
</template>
|
|
</VnSubToolbar>
|
|
<div class="route-list">
|
|
<VnPaginate
|
|
:key="refreshKey"
|
|
data-key="RouteList"
|
|
url="Routes/filter"
|
|
:order="['created DESC', 'id DESC']"
|
|
:limit="20"
|
|
auto-load
|
|
>
|
|
<template #body="{ rows }">
|
|
<div class="q-pa-md">
|
|
<QTable
|
|
v-model:selected="selectedRows"
|
|
:columns="columns"
|
|
:rows="rows"
|
|
flat
|
|
row-key="id"
|
|
selection="multiple"
|
|
:rows-per-page-options="[0]"
|
|
hide-pagination
|
|
:pagination="{ sortBy: 'ID', descending: true }"
|
|
>
|
|
<template #body-cell-worker="props">
|
|
<QTd :props="props">
|
|
{{ props.row?.workerUserName }}
|
|
<QPopupEdit
|
|
:model-value="props.row.workerFk"
|
|
v-slot="scope"
|
|
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 }">
|
|
<QItem
|
|
v-bind="itemProps"
|
|
class="q-pa-xs row items-center"
|
|
>
|
|
<QItemSection
|
|
class="col-9 justify-center"
|
|
>
|
|
<span>{{ opt.name }}</span>
|
|
<span class="text-grey">{{
|
|
opt.nickname
|
|
}}</span>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-agency="props">
|
|
<QTd :props="props">
|
|
{{ props.row?.agencyName }}
|
|
<QPopupEdit
|
|
:model-value="props.row.agencyModeFk"
|
|
v-slot="scope"
|
|
buttons
|
|
@update:model-value="
|
|
(agency) => updateAgency(props.row, agency)
|
|
"
|
|
>
|
|
<VnSelectFilter
|
|
:label="t('Agency')"
|
|
v-model="scope.value"
|
|
:options="agencyList"
|
|
option-value="id"
|
|
option-label="name"
|
|
hide-selected
|
|
autofocus
|
|
:emit-value="false"
|
|
:rules="validate('route.agencyFk')"
|
|
:is-clearable="false"
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-vehicle="props">
|
|
<QTd :props="props">
|
|
{{ props.row?.vehiclePlateNumber }}
|
|
<QPopupEdit
|
|
:model-value="props.row.vehicleFk"
|
|
v-slot="scope"
|
|
buttons
|
|
@update:model-value="
|
|
(vehicle) => updateVehicle(props.row, vehicle)
|
|
"
|
|
>
|
|
<VnSelectFilter
|
|
:label="t('Vehicle')"
|
|
v-model="scope.value"
|
|
:options="vehicleList"
|
|
option-value="id"
|
|
option-label="numberPlate"
|
|
hide-selected
|
|
autofocus
|
|
:emit-value="false"
|
|
:rules="validate('route.vehicleFk')"
|
|
:is-clearable="false"
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-date="props">
|
|
<QTd :props="props">
|
|
{{ toDate(props.row?.created) }}
|
|
<QPopupEdit
|
|
v-model="props.row.created"
|
|
v-slot="scope"
|
|
@update:model-value="updateRoute(props.row)"
|
|
buttons
|
|
>
|
|
<VnInputDate
|
|
v-model="scope.value"
|
|
autofocus
|
|
:label="t('Date')"
|
|
:rules="validate('route.created')"
|
|
:is-clearable="false"
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-description="props">
|
|
<QTd :props="props">
|
|
{{ props.row?.description }}
|
|
<QPopupEdit
|
|
v-model="props.row.description"
|
|
v-slot="scope"
|
|
@update:model-value="updateRoute(props.row)"
|
|
buttons
|
|
>
|
|
<VnInput
|
|
v-model="scope.value"
|
|
autofocus
|
|
:label="t('Description')"
|
|
:rules="validate('route.description')"
|
|
:is-clearable="false"
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-started="props">
|
|
<QTd :props="props">
|
|
{{ toHour(props.row.started) }}
|
|
<QPopupEdit
|
|
v-model="props.row.started"
|
|
v-slot="scope"
|
|
buttons
|
|
@update:model-value="updateRoute(props.row)"
|
|
>
|
|
<VnInputTime
|
|
v-model="scope.value"
|
|
autofocus
|
|
:label="t('Hour started')"
|
|
:rules="validate('route.started')"
|
|
:is-clearable="false"
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-finished="props">
|
|
<QTd :props="props">
|
|
{{ toHour(props.row.finished) }}
|
|
<QPopupEdit
|
|
v-model="props.row.finished"
|
|
v-slot="scope"
|
|
buttons
|
|
@update:model-value="updateRoute(props.row)"
|
|
>
|
|
<VnInputTime
|
|
v-model="scope.value"
|
|
autofocus
|
|
:label="t('Hour finished')"
|
|
:rules="validate('route.finished')"
|
|
:is-clearable="false"
|
|
@keyup.enter="scope.set"
|
|
@focus="($event) => $event.target.select()"
|
|
/>
|
|
</QPopupEdit>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-actions="props">
|
|
<QTd :props="props">
|
|
<div class="flex items-center table-actions">
|
|
<QIcon
|
|
name="vn:ticketAdd"
|
|
size="xs"
|
|
color="primary"
|
|
class="cursor-pointer"
|
|
>
|
|
<QTooltip>{{ t('Add ticket') }}</QTooltip>
|
|
</QIcon>
|
|
<QIcon
|
|
name="preview"
|
|
size="xs"
|
|
color="primary"
|
|
@click="previewRoute(props?.row?.id)"
|
|
class="cursor-pointer"
|
|
>
|
|
<QTooltip>{{ t('Preview') }}</QTooltip>
|
|
</QIcon>
|
|
</div>
|
|
</QTd>
|
|
</template>
|
|
</QTable>
|
|
</div>
|
|
</template>
|
|
</VnPaginate>
|
|
</div>
|
|
<QPageSticky :offset="[20, 20]">
|
|
<RouterLink :to="{ name: 'RouteCreate' }">
|
|
<QBtn fab icon="add" color="primary" />
|
|
<QTooltip>
|
|
{{ t('newRoute') }}
|
|
</QTooltip>
|
|
</RouterLink>
|
|
</QPageSticky>
|
|
</QPage>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.route-list {
|
|
width: 100%;
|
|
}
|
|
|
|
.table-actions {
|
|
gap: 12px;
|
|
}
|
|
</style>
|
|
<i18n>
|
|
en:
|
|
newRoute: New Route
|
|
es:
|
|
ID: ID
|
|
Worker: Trabajador
|
|
Agency: Agencia
|
|
Vehicle: Vehículo
|
|
Date: Fecha
|
|
Description: Descripción
|
|
Hour started: Hora inicio
|
|
Hour finished: Hora fin
|
|
newRoute: Nueva Ruta
|
|
Clone Selected Routes: Clonar rutas seleccionadas
|
|
Select the starting date: Seleccione la fecha de inicio
|
|
Stating date: Fecha de inicio
|
|
Cancel: Cancelar
|
|
Clone: Clonar
|
|
Mark as served: Marcar como servidas
|
|
Add ticket: Añadir tickets
|
|
Preview: Vista previa
|
|
</i18n>
|