Create route list

This commit is contained in:
Kevin Martinez 2024-01-12 10:03:18 -04:00
parent 3905dd05df
commit 3d98bf578e
8 changed files with 548 additions and 1 deletions

View File

@ -0,0 +1,108 @@
<script setup>
import {computed, ref} from 'vue';
import { toHour} from 'src/filters';
import {useI18n} from "vue-i18n";
import isValidDate from "filters/isValidDate";
const props = defineProps({
modelValue: {
type: String,
default: null,
},
readonly: {
type: Boolean,
default: false,
},
isOutlined: {
type: Boolean,
default: false,
},
});
const { t } = useI18n();
const emit = defineEmits(['update:modelValue']);
const value = computed({
get() {
return props.modelValue;
},
set(value) {
const [hours, minutes] = value.split(':')
const date = new Date()
date.setUTCHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0)
emit('update:modelValue', value ? date.toISOString() : null);
},
});
const onDateUpdate = (date) => {
internalValue.value = date;
};
const save = () => {
value.value = internalValue.value;
};
const formatTime = (dateString) => {
if (!isValidDate(dateString)){
return ''
}
const date = new Date(dateString || '');
return `${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}`;
};
const internalValue = ref(formatTime(value))
const styleAttrs = computed(() => {
return props.isOutlined
? {
dense: true,
outlined: true,
rounded: true,
}
: {};
});
</script>
<template>
<QInput
class="vn-input-time"
rounded
readonly
:model-value="toHour(value)"
v-bind="{ ...$attrs, ...styleAttrs }"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
:no-parent-event="props.readonly"
>
<QTime
:format24h="false"
:model-value="formatTime(value)"
@update:model-value="onDateUpdate"
>
<div class="row items-center justify-end q-gutter-sm">
<QBtn :label="t('Cancel')" color="primary" flat v-close-popup />
<QBtn label="Ok" color="primary" flat @click="save" v-close-popup />
</div>
</QTime>
</QPopupProxy>
</QIcon>
</template>
</QInput>
</template>
<style lang="scss">
.vn-input-time.q-field--standard.q-field--readonly .q-field__control:before {
border-bottom-style: solid;
}
.vn-input-time.q-field--outlined.q-field--readonly .q-field__control:before {
border-style: solid;
}
</style>
<i18n>
es:
Cancel: Cancelar
</i18n>

View File

@ -8,11 +8,13 @@ import toPercentage from './toPercentage';
import toLowerCamel from './toLowerCamel'; import toLowerCamel from './toLowerCamel';
import dashIfEmpty from './dashIfEmpty'; import dashIfEmpty from './dashIfEmpty';
import dateRange from './dateRange'; import dateRange from './dateRange';
import toHour from './toHour';
export { export {
toLowerCase, toLowerCase,
toLowerCamel, toLowerCamel,
toDate, toDate,
toHour,
toDateString, toDateString,
toDateHour, toDateHour,
toRelativeDate, toRelativeDate,

View File

@ -0,0 +1,3 @@
export default function isValidDate(date) {
return !isNaN(new Date(date).getTime());
}

16
src/filters/toHour.js Normal file
View File

@ -0,0 +1,16 @@
import isValidDate from 'filters/isValidDate';
export default function toHour(date) {
if (!isValidDate(date)) {
return '--:--';
}
const dateHour = new Date(date);
let hours = dateHour.getUTCHours();
hours = hours % 12;
hours = hours ? hours : 12;
let minutes = dateHour.getUTCMinutes();
minutes = minutes < 10 ? minutes.toString().padStart(2, '0') : minutes;
return `${hours}:${minutes} ${dateHour.getUTCHours() >= 12 ? 'PM' : 'AM'}`;
}

View File

@ -807,6 +807,7 @@ export default {
pageTitles: { pageTitles: {
routes: 'Routes', routes: 'Routes',
cmrsList: 'External CMRs list', cmrsList: 'External CMRs list',
RouteList: 'List'
}, },
cmr: { cmr: {
list: { list: {

View File

@ -806,6 +806,7 @@ export default {
pageTitles: { pageTitles: {
routes: 'Rutas', routes: 'Rutas',
cmrsList: 'Listado de CMRs externos', cmrsList: 'Listado de CMRs externos',
RouteList: 'Listado'
}, },
cmr: { cmr: {
list: { list: {

View File

@ -0,0 +1,407 @@
<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";
const stateStore = useStateStore();
// const router = useRouter();
// const quasar = useQuasar();
const { t } = useI18n();
const { validate } = useValidator();
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',
},
]);
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)
}
</script>
<template>
<template v-if="stateStore.isHeaderMounted()">
<!-- <Teleport to="#searchbar">-->
<!-- <ShelvingSearchbar />-->
<!-- </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>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<!-- <QScrollArea class="fit text-grey-8">-->
<!-- <ShelvingFilter data-key="ShelvingList" />-->
<!-- </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 q-pa-md">
<QToolbar class="q-pa-none q-mb-sm justify-end">
<QBtn
icon="refresh"
color="primary"
class="q-mr-sm"
@click="refreshKey++"
/>
</QToolbar>
<VnPaginate
:key="refreshKey"
data-key="RouteList"
url="Routes/filter"
:order="['created DESC', 'id DESC']"
:limit="20"
:user-params="params"
auto-load
>
<template #body="{ rows }">
<QTable
v-model:selected="selectedRows"
:columns="columns"
:rows="rows"
flat
row-key="id"
hide-pagination
selection="multiple"
>
<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>
</QTable>
</template>
</VnPaginate>
<!-- <QPageSticky :offset="[20, 20]">-->
<!-- <RouterLink :to="{ name: 'ShelvingCreate' }">-->
<!-- <QBtn fab icon="add" color="primary" />-->
<!-- <QTooltip>-->
<!-- {{ t('shelving.list.newShelving') }}-->
<!-- </QTooltip>-->
<!-- </RouterLink>-->
<!-- </QPageSticky>-->
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
ID: ID
Worker: Trabajador
Agency: Agencia
Vehicle: Vehículo
Date: Fecha
Description: Descripción
Hour started: Hora inicio
Hour finished: Hora fin
</i18n>

View File

@ -10,7 +10,7 @@ export default {
component: RouterView, component: RouterView,
redirect: { name: 'RouteMain' }, redirect: { name: 'RouteMain' },
menus: { menus: {
main: ['CmrList'], main: ['RouteList', 'CmrList'],
card: [], card: [],
}, },
children: [ children: [
@ -29,6 +29,15 @@ export default {
}, },
component: () => import('src/pages/Route/Cmr/CmrList.vue'), component: () => import('src/pages/Route/Cmr/CmrList.vue'),
}, },
{
path: 'list',
name: 'RouteList',
meta: {
title: 'RouteList',
icon: 'vn:delivery',
},
component: () => import('src/pages/Route/RouteList.vue'),
},
], ],
}, },
], ],