0
0
Fork 0

Merge pull request '7129-finishMigration' (!549) from 7129-finishMigration into dev

Reviewed-on: verdnatura/salix-front#549
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
This commit is contained in:
Pablo Natek 2024-07-15 09:26:43 +00:00
commit 85a32a6029
23 changed files with 547 additions and 933 deletions

View File

@ -14,5 +14,5 @@
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"cSpell.words": ["axios"]
"cSpell.words": ["axios", "composables"]
}

View File

@ -98,19 +98,17 @@ defineExpose({
});
async function fetch(data) {
if (data && Array.isArray(data)) {
let $index = 0;
data.map((d) => (d.$index = $index++));
}
resetData(data);
emit('onFetch', data);
return data;
}
function resetData(data) {
if (!data) return;
if (data && Array.isArray(data)) {
let $index = 0;
data.map((d) => (d.$index = $index++));
}
originalData.value = JSON.parse(JSON.stringify(data));
formData.value = JSON.parse(JSON.stringify(data));
@ -299,7 +297,7 @@ watch(formUrl, async () => {
:url="url"
:limit="limit"
@on-fetch="fetch"
@on-change="resetData"
@on-change="fetch"
:skeleton="false"
ref="vnPaginateRef"
v-bind="$attrs"

View File

@ -90,7 +90,7 @@ const $props = defineProps({
});
const emit = defineEmits(['onFetch', 'onDataSaved']);
const modelValue = computed(
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`,
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`
).value;
const componentIsRendered = ref(false);
const arrayData = useArrayData(modelValue);
@ -137,7 +137,7 @@ onMounted(async () => {
JSON.stringify(newVal) !== JSON.stringify(originalData.value);
isResetting.value = false;
},
{ deep: true },
{ deep: true }
);
}
});
@ -145,7 +145,7 @@ onMounted(async () => {
if (!$props.url)
watch(
() => arrayData.store.data,
(val) => updateAndEmit('onFetch', val),
(val) => updateAndEmit('onFetch', val)
);
watch(formUrl, async () => {
@ -239,7 +239,7 @@ function filter(value, update, filterOptions) {
(ref) => {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
},
}
);
}
@ -261,13 +261,7 @@ defineExpose({
</script>
<template>
<div class="column items-center full-width">
<QForm
@submit="save"
@reset="reset"
class="q-pa-md"
id="formModel"
>
<QForm @submit="save" @reset="reset" class="q-pa-md" id="formModel">
<QCard>
<slot
v-if="formData"
@ -276,70 +270,72 @@ defineExpose({
:validate="validate"
:filter="filter"
/>
<SkeletonForm v-else/>
<SkeletonForm v-else />
</QCard>
</QForm>
</div>
<Teleport
to="#st-actions"
v-if="stateStore?.isSubToolbarShown() && componentIsRendered"
v-if="
$props.defaultActions &&
stateStore?.isSubToolbarShown() &&
componentIsRendered
"
>
<div v-if="$props.defaultActions">
<QBtnGroup push class="q-gutter-x-sm">
<slot name="moreActions" />
<QBtn
:label="tMobile(defaultButtons.reset.label)"
:color="defaultButtons.reset.color"
:icon="defaultButtons.reset.icon"
flat
@click="reset"
:disable="!hasChanges"
:title="t(defaultButtons.reset.label)"
/>
<QBtnDropdown
v-if="$props.goTo"
@click="saveAndGo"
:label="tMobile('globals.saveAndContinue')"
:title="t('globals.saveAndContinue')"
:disable="!hasChanges"
color="primary"
icon="save"
split
>
<QList>
<QItem
clickable
v-close-popup
@click="save"
:title="t('globals.save')"
>
<QItemSection>
<QItemLabel>
<QIcon
name="save"
color="white"
class="q-mr-sm"
size="sm"
/>
{{ t('globals.save').toUpperCase() }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QBtnDropdown>
<QBtn
v-else
:label="tMobile('globals.save')"
color="primary"
icon="save"
@click="save"
:disable="!hasChanges"
:title="t(defaultButtons.save.label)"
/>
</QBtnGroup>
</div>
<QBtnGroup push class="q-gutter-x-sm">
<slot name="moreActions" />
<QBtn
:label="tMobile(defaultButtons.reset.label)"
:color="defaultButtons.reset.color"
:icon="defaultButtons.reset.icon"
flat
@click="reset"
:disable="!hasChanges"
:title="t(defaultButtons.reset.label)"
/>
<QBtnDropdown
v-if="$props.goTo"
@click="saveAndGo"
:label="tMobile('globals.saveAndContinue')"
:title="t('globals.saveAndContinue')"
:disable="!hasChanges"
color="primary"
icon="save"
split
>
<QList>
<QItem
clickable
v-close-popup
@click="save"
:title="t('globals.save')"
>
<QItemSection>
<QItemLabel>
<QIcon
name="save"
color="white"
class="q-mr-sm"
size="sm"
/>
{{ t('globals.save').toUpperCase() }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QBtnDropdown>
<QBtn
v-else
:label="tMobile('globals.save')"
color="primary"
icon="save"
@click="save"
:disable="!hasChanges"
:title="t(defaultButtons.save.label)"
/>
</QBtnGroup>
</Teleport>
<QInnerLoading
:showing="isLoading"
:label="t('globals.pleaseWait')"

View File

@ -23,18 +23,15 @@ const formModelRef = ref(null);
const closeButton = ref(null);
const onDataSaved = (formData, requestResponse) => {
closeForm();
if (closeButton.value) closeButton.value.click();
emit('onDataSaved', formData, requestResponse);
};
const isLoading = computed(() => formModelRef.value?.isLoading);
const closeForm = async () => {
if (closeButton.value) closeButton.value.click();
};
defineExpose({
isLoading,
onDataSaved,
});
</script>

View File

@ -8,6 +8,7 @@ import VnSelect from 'components/common/VnSelect.vue';
import VnSelectCache from 'components/common/VnSelectCache.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnComponent from 'components/common/VnComponent.vue';
const model = defineModel(undefined, { required: true });
@ -86,6 +87,15 @@ const defaultComponents = {
label: $props.showLabel && $props.column.label,
},
},
time: {
component: markRaw(VnInputTime),
attrs: {
disable: !$props.isEditable,
},
forceAttrs: {
label: $props.showLabel && $props.column.label,
},
},
checkbox: {
component: markRaw(QCheckbox),
attrs: (prop) => {

View File

@ -3,17 +3,17 @@ import { ref, onMounted, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore';
import FormModelPopup from 'components/FormModelPopup.vue';
import CrudModel from 'src/components/CrudModel.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnLv from 'components/ui/VnLv.vue';
import CrudModel from 'src/components/CrudModel.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnTableColumn from 'components/VnTable/VnColumn.vue';
import VnTableFilter from 'components/VnTable/VnFilter.vue';
import VnTableChip from 'components/VnTable/VnChip.vue';
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
import VnLv from 'components/ui/VnLv.vue';
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
const $props = defineProps({
@ -34,7 +34,7 @@ const $props = defineProps({
default: true,
},
rowClick: {
type: Function,
type: [Function, Boolean],
default: null,
},
redirect: {
@ -81,6 +81,10 @@ const $props = defineProps({
type: Object,
default: () => ({}),
},
tableHeight: {
type: String,
default: '90vh',
},
});
const { t } = useI18n();
const stateStore = useStateStore();
@ -137,12 +141,6 @@ watch(
(val) => setUserParams(val)
);
const rowClickFunction = computed(() => {
if ($props.rowClick) return $props.rowClick;
if ($props.redirect) return ({ id }) => redirectFn(id);
return () => {};
});
const isTableMode = computed(() => mode.value == TABLE_MODE);
function setUserParams(watchedParams) {
@ -194,6 +192,12 @@ function splitColumns(columns) {
}
}
const rowClickFunction = computed(() => {
if ($props.rowClick != undefined) return $props.rowClick;
if ($props.redirect) return ({ id }) => redirectFn(id);
return () => {};
});
function redirectFn(id) {
router.push({ path: `/${$props.redirect}/${id}` });
}
@ -249,7 +253,6 @@ defineExpose({
:data-key="$attrs['data-key']"
:search-button="true"
v-model="params"
:disable-submit-event="true"
:search-url="searchUrl"
:redirect="!!redirect"
>
@ -311,14 +314,14 @@ defineExpose({
table-header-class="bg-header"
card-container-class="grid-three"
flat
:style="isTableMode && 'max-height: 90vh'"
:style="isTableMode && `max-height: ${tableHeight}`"
virtual-scroll
@virtual-scroll="
(event) =>
event.index > rows.length - 2 &&
CrudModelRef.vnPaginateRef.paginate()
"
@row-click="(_, row) => rowClickFunction(row)"
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
@update:selected="emit('update:selected', $event)"
>
<template #top-left v-if="!$props.withoutHeader">
@ -586,8 +589,12 @@ defineExpose({
<i18n>
en:
status: Status
table view: Table view
grid view: Grid view
es:
status: Estados
table view: Vista en tabla
grid view: Vista en cuadrícula
</i18n>
<style lang="scss">
@ -597,7 +604,7 @@ es:
}
.bg-header {
background-color: #5d5d5d;
background-color: var(--vn-header-color);
color: var(--vn-text-color);
}
@ -610,7 +617,7 @@ es:
.q-table--dark tr,
.q-table--dark th,
.q-table--dark td {
border-color: #222222;
border-color: var(--vn-section-color);
}
.q-table__container > div:first-child {
@ -680,7 +687,7 @@ es:
right: 0;
}
td.sticky {
background-color: var(--q-dark);
background-color: var(--vn-section-color);
z-index: 1;
}
}

View File

@ -119,6 +119,7 @@ watch(
>
<QDate
v-model="popupDate"
:landscape="true"
:today-btn="true"
@update:model-value="
(date) => {

View File

@ -43,7 +43,7 @@ const formattedTime = computed({
if (time?.length > 5) time = dateToTime(time);
if (!props.timeOnly) {
const hours = time.split(':');
const date = new Date();
const date = new Date(model.value);
date.setHours(hours[0], hours[1], 0);
time = date.toISOString();
}
@ -100,13 +100,7 @@ watch(
self="top start"
:no-focus="true"
>
<QTime
:format24h="false"
v-model="formattedTime"
mask="HH:mm"
landscape
now-btn
/>
<QTime v-model="formattedTime" mask="HH:mm" landscape now-btn />
</QMenu>
</QInput>
</div>

View File

@ -18,19 +18,18 @@ const $props = defineProps({
},
searchButton: {
type: Boolean,
required: false,
default: false,
},
showAll: {
type: Boolean,
default: true,
},
unremovableParams: {
unRemovableParams: {
type: Array,
required: false,
default: () => [],
description:
'Algunos filtros vienen con parametros de búsqueda por default y necesitan tener si o si un valor, por eso de ser necesario, esta prop nos sirve para saber que filtros podemos remover y cuales no',
description: `Some filters come with default search parameters and require a value.
This prop helps us determine which filters can be removed and which cannot.`,
},
exprBuilder: {
type: Function,
@ -113,7 +112,6 @@ async function search(evt) {
isLoading.value = true;
const filter = { ...userParams.value };
store.userParamsChanged = true;
arrayData.reset(['skip', 'filter.skip', 'page']);
const { params: newParams } = await arrayData.addFilter({ params: userParams.value });
userParams.value = newParams;
@ -140,7 +138,7 @@ async function clearFilters() {
arrayData.reset(['skip', 'filter.skip', 'page']);
// Filtrar los params no removibles
const removableFilters = Object.keys(userParams.value).filter((param) =>
$props.unremovableParams.includes(param)
$props.unRemovableParams.includes(param)
);
const newParams = {};
// Conservar solo los params que no son removibles
@ -249,7 +247,7 @@ function formatValue(value) {
<VnFilterPanelChip
v-for="chip of tags"
:key="chip.label"
:removable="!unremovableParams.includes(chip.label)"
:removable="!unRemovableParams.includes(chip.label)"
@remove="remove(chip.label)"
>
<slot name="tags" :tag="chip" :format-fn="formatValue">

View File

@ -30,6 +30,7 @@ onBeforeUnmount(() => stateStore.toggleSubToolbar());
<template>
<QToolbar
id="subToolbar"
class="justify-end sticky"
v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
>

View File

@ -74,7 +74,9 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
};
let exprFilter;
let userParams = { ...store.userParams };
if (store?.exprBuilder) {
const where = buildFilter(userParams, (param, value) => {
const res = store.exprBuilder(param, value);
@ -157,6 +159,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
if (filter) store.filter = filter;
let userParams = { ...store.userParams, ...params };
userParams = sanitizerParams(userParams, store?.exprBuilder);
store.userParams = userParams;

View File

@ -4,9 +4,10 @@
body.body--light {
--font-color: black;
--vn-header-color: #cecece;
--vn-page-color: #ffffff;
--vn-section-color: #e0e0e0;
--vn-section-hover-color: #b9b9b9;
--vn-page-color: #ffffff;
--vn-text-color: var(--font-color);
--vn-label-color: #5f5f5f;
--vn-accent-color: #e7e3e3;
@ -18,6 +19,7 @@ body.body--light {
}
}
body.body--dark {
--vn-header-color: #5d5d5d;
--vn-page-color: #222;
--vn-section-color: #3d3d3d;
--vn-section-hover-color: #747474;

View File

@ -78,6 +78,7 @@ const columns = computed(() => [
:columns="columns"
:right-search="false"
:use-model="true"
redirect="agency"
default-mode="card"
/>
</template>

View File

@ -1,162 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const countries = ref();
const warehouses = ref();
</script>
<template>
<FetchData url="Countries" @on-fetch="(data) => (countries = data)" auto-load />
<FetchData url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params }">
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.cmrFk')"
v-model="params.cmrFk"
is-outlined
>
<template #prepend>
<QIcon name="article" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('route.cmr.list.hasCmrDms')"
v-model="params.hasCmrDms"
lazy-rules
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.ticketFk')"
v-model="params.ticketFk"
is-outlined
>
<template #prepend>
<QIcon name="vn:ticket" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.routeFk')"
v-model="params.routeFk"
is-outlined
>
<template #prepend>
<QIcon name="vn:delivery" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('route.cmr.list.clientFk')"
v-model="params.clientFk"
is-outlined
>
<template #prepend>
<QIcon name="vn:client" size="sm"></QIcon>
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem>
<QItemSection v-if="!countries">
<QSkeleton type="QInput" class="full-width" />
</QItemSection>
<QItemSection v-if="countries">
<VnSelect
:label="t('route.cmr.list.country')"
v-model="params.country"
:options="countries"
option-label="name"
option-value="id"
transition-show="jump-down"
transition-hide="jump-up"
emit-value
map-options
dense
outlined
rounded
>
<template #prepend>
<QIcon name="flag" size="sm"></QIcon>
</template>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<VnSelect
:label="t('globals.warehouse')"
:options="warehouses"
hide-selected
option-label="name"
option-value="id"
v-model="params.warehouseFk"
rounded
dense
outlined
>
</VnSelect>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
v-model="params.shipped"
:label="t('route.cmr.list.shipped')"
is-outlined
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
cmrFk: Cmr id
hasCmrDms: Attached in gestdoc
ticketFk: Ticketd id
country: Country
clientFk: Client id
shipped: Preparation date
es:
params:
cmrFk: Id cmr
hasCmrDms: Gestdoc
ticketFk: Id ticket
country: País
clientFk: Id cliente
shipped: Fecha preparación
</i18n>

View File

@ -2,82 +2,93 @@
import { onBeforeMount, onMounted, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Notify } from 'quasar';
import axios from 'axios';
import VnPaginate from 'components/ui/VnPaginate.vue';
import { useSession } from 'src/composables/useSession';
import { toDate } from 'filters/index';
import CmrFilter from './CmrFilter.vue';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import { useStateStore } from 'src/stores/useStateStore';
import axios from 'axios';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnTable from 'components/VnTable/VnTable.vue';
const { t } = useI18n();
const { getTokenMultimedia } = useSession();
const token = getTokenMultimedia();
const state = useStateStore();
const selected = ref([]);
const warehouses = ref([]);
const selectedRows = ref([]);
const columns = computed(() => [
{
align: 'left',
name: 'cmrFk',
label: t('route.cmr.list.cmrFk'),
field: (row) => row.cmrFk,
sortable: true,
chip: {
condition: () => true,
},
isId: true,
},
{
align: 'left',
name: 'hasCmrDms',
label: t('route.cmr.list.hasCmrDms'),
field: (row) => row.hasCmrDms,
align: 'center',
sortable: true,
component: 'checkbox',
cardVisible: true,
},
{
name: 'ticketFk',
label: t('route.cmr.list.ticketFk'),
field: (row) => row.ticketFk,
sortable: true,
name: 'ticketFk',
},
{
name: 'routeFkFk',
label: t('route.cmr.list.routeFk'),
field: (row) => row.routeFk,
sortable: true,
name: 'routeFk',
},
{
name: 'clientFk',
label: t('route.cmr.list.clientFk'),
field: (row) => row.clientFk,
sortable: true,
name: 'clientFk',
},
{
name: 'country',
label: t('route.cmr.list.country'),
field: (row) => row.country,
align: 'left',
sortable: true,
label: t('route.cmr.list.country'),
name: 'country',
cardVisible: true,
},
{
name: 'shipped',
label: t('route.cmr.list.shipped'),
field: (row) => toDate(row.shipped),
align: 'center',
sortable: true,
label: t('route.cmr.list.shipped'),
name: 'shipped',
cardVisible: true,
component: 'date',
columnFilter: {
alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
},
{
align: 'center',
name: 'warehouseFk',
label: t('globals.warehouse'),
field: ({ warehouseFk }) => warehouseFk,
align: 'center',
sortable: true,
component: 'select',
attrs: {
url: 'warehouses',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
},
{
name: 'icons',
align: 'center',
field: (row) => row.cmrFk,
align: 'right',
name: 'tableActions',
actions: [
{
title: t('Ver cmr'),
icon: 'visibility',
isPrimary: true,
action: (row) => window.open(getCmrUrl(row?.cmrFk), '_blank'),
},
],
},
]);
@ -95,7 +106,7 @@ function getCmrUrl(value) {
return `${getApiUrl()}/api/Routes/${value}/cmr?access_token=${token}`;
}
function downloadPdfs() {
if (!selected.value.length) {
if (!selectedRows.value.length) {
Notify.create({
message: t('globals.noSelectedRows'),
type: 'warning',
@ -103,43 +114,51 @@ function downloadPdfs() {
return;
}
let cmrs = [];
for (let value of selected.value) cmrs.push(value.cmrFk);
for (let value of selectedRows.value) cmrs.push(value.cmrFk);
// prettier-ignore
return window.open(`${getApiUrl()}/api/Routes/downloadCmrsZip?ids=${cmrs.join(',')}&access_token=${token}`);
}
</script>
<template>
<VnSubToolbar>
<template #st-actions>
<QBtn
icon="cloud_download"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="downloadPdfs"
>
<QTooltip>{{ t('route.cmr.list.downloadCmrs') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<VnTable
ref="tableRef"
data-key="CmrList"
url="Routes/cmrs"
order="cmrFk DESC"
:columns="columns"
:right-search="true"
:use-model="true"
default-mode="card"
/>
default-mode="table"
v-model:selected="selectedRows"
table-height="85vh"
:table="{
'row-key': 'cmrFk',
selection: 'multiple',
}"
:disable-option="{ card: true }"
>
<template #column-ticketFk="{ row }">
<span class="link" @click.stop>
{{ row.ticketFk }}
<TicketDescriptorProxy :id="row.ticketFk" />
</span>
</template>
<template #column-clientFk="{ row }">
<span class="link" @click.stop>
{{ row.ticketFk }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
</VnTable>
</template>
<style lang="scss" scoped>
.list {
padding-top: 15px;
padding-bottom: 15px;
max-width: 1000px;
width: 100%;
}
.grid-style-transition {
transition: transform 0.28s, background-color 0.28s;
}
#true {
background-color: $positive;
}
#false {
background-color: $negative;
}
:deep(.q-table th) {
max-width: 80px;
}
:deep(.q-table th:nth-child(3)) {
max-width: 100px;
}
</style>

View File

@ -1,88 +0,0 @@
<script setup>
import { useDialogPluginComponent } from 'quasar';
import { useI18n } from 'vue-i18n';
import { reactive, ref } from 'vue';
import axios from 'axios';
import FetchData from 'components/FetchData.vue';
import RoadmapAddStopForm from 'pages/Route/Roadmap/RoadmapAddStopForm.vue';
const emit = defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide } = useDialogPluginComponent();
const { t } = useI18n();
const props = defineProps({
roadmapFk: {
type: Number,
required: true,
},
});
const isLoading = ref(false);
const roadmapStopForm = reactive({
roadmapFk: Number(props.roadmapFk) || 0,
warehouseFk: null,
eta: new Date().toISOString(),
description: '',
});
const updateDefaultStop = (data) => {
const eta = new Date(data.etd);
eta.setDate(eta.getDate() + 1);
roadmapStopForm.eta = eta.toISOString();
};
const onSave = async () => {
isLoading.value = true;
try {
await axios.post('RoadmapStops', { ...roadmapStopForm });
emit('ok');
} finally {
isLoading.value = false;
emit('hide');
}
};
</script>
<template>
<FetchData
:url="`Roadmaps/${roadmapFk}`"
auto-load
:filter="{ fields: ['etd'] }"
@on-fetch="updateDefaultStop"
/>
<QDialog ref="dialogRef" @hide="onDialogHide">
<QCard class="q-pa-lg">
<QCardSection class="row absolute absolute-top z-fullscreen">
<QSpace />
<QBtn icon="close" flat round dense v-close-popup />
</QCardSection>
<RoadmapAddStopForm
:roadmap-fk="roadmapFk"
:form-data="roadmapStopForm"
layout="dialog"
/>
<QCardActions align="right">
<QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup />
<QBtn
:label="t('globals.confirm')"
color="primary"
:loading="isLoading"
@click="onSave"
unelevated
/>
</QCardActions>
</QCard>
</QDialog>
</template>
<style lang="scss" scoped>
.card-section {
gap: 16px;
}
</style>
<i18n>
es:
Warehouse: Almacén
ETA date: Fecha ETA
ETA time: Hora ETA
Description: Descripción
</i18n>

View File

@ -1,45 +0,0 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnRow from 'components/ui/VnRow.vue';
import FormModel from 'components/FormModel.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
const { t } = useI18n();
const router = useRouter();
const defaultInitialData = {
etd: Date.vnNew().toISOString(),
};
const onSave = (data, response) => {
router.push({ name: 'RoadmapSummary', params: { id: response?.id } });
};
</script>
<template>
<VnSubToolbar />
<FormModel
:url="null"
url-create="Roadmaps"
model="roadmap"
:observe-form-changes="false"
:auto-load="false"
:form-initial-data="defaultInitialData"
@on-data-saved="onSave"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<VnInput v-model="data.name" :label="t('Roadmap')" clearable />
<VnInputDate v-model="data.etd" :label="t('ETD date')" />
<VnInputTime v-model="data.etd" :label="t('ETD hour')" />
</VnRow>
</template>
</FormModel>
</template>
<i18n>
es:
Roadmap: Troncal
ETD date: Fecha ETD
ETD hour: Hora ETD
</i18n>

View File

@ -2,14 +2,14 @@
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { QIcon, useQuasar } from 'quasar';
import { QIcon } from 'quasar';
import { dashIfEmpty, toDateHourMin } from 'src/filters';
import { useStateStore } from 'stores/useStateStore';
import VnLv from 'components/ui/VnLv.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import CardSummary from 'components/ui/CardSummary.vue';
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
import VnLinkPhone from 'components/ui/VnLinkPhone.vue';
import RoadmapAddStopDialog from 'pages/Route/Roadmap/RoadmapAddStopDialog.vue';
const $props = defineProps({
id: {
@ -20,7 +20,6 @@ const $props = defineProps({
const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n();
const quasar = useQuasar();
const summary = ref(null);
const entityId = computed(() => $props.id || route.params.id);
@ -37,7 +36,7 @@ const columns = ref([
{
name: 'address',
label: t('Address'),
field: (row) => dashIfEmpty(row?.address?.nickname),
field: (row) => dashIfEmpty(row?.address?.addressFk),
sortable: true,
align: 'left',
},
@ -69,15 +68,6 @@ const filter = {
],
where: { id: entityId },
};
const openAddStopDialog = () => {
quasar
.dialog({
component: RoadmapAddStopDialog,
componentProps: { roadmapFk: entityId.value },
})
.onOk(() => summary.value.fetch());
};
</script>
<template>
@ -98,6 +88,10 @@ const openAddStopDialog = () => {
</template>
<template #body="{ entity }">
<QCard class="vn-one">
<VnTitle
:url="`#/route/roadmap/${entityId}/basic-data`"
:text="t('Basic Data')"
/>
<VnLv :label="t('Carrier')">
<template #value>
<span class="link" v-if="entity?.supplier?.id">
@ -115,8 +109,6 @@ const openAddStopDialog = () => {
:label="t('Trailer Plate')"
:value="dashIfEmpty(entity?.trailerPlate)"
/>
</QCard>
<QCard class="vn-one">
<VnLv :label="t('Phone')" :value="dashIfEmpty(entity?.phone)">
<template #value>
<span>
@ -139,26 +131,10 @@ const openAddStopDialog = () => {
/>
</QCard>
<QCard class="vn-max">
<div class="flex items-center">
<a class="header" :href="`#/route/roadmap/${entityId}/stops`">
{{ t('Stops') }}
<QIcon name="open_in_new" color="primary">
<QTooltip>
{{ t('Go to stops') }}
</QTooltip>
</QIcon>
</a>
<QIcon
name="add_circle"
color="primary"
class="q-ml-lg header cursor-pointer"
@click.stop="openAddStopDialog"
>
<QTooltip>
{{ t('Add stop') }}
</QTooltip>
</QIcon>
</div>
<VnTitle
:url="`#/route/roadmap/${entityId}/stops`"
:text="t('Stops')"
/>
<QTable
:columns="columns"
:rows="entity?.roadmapStop"

View File

@ -1,33 +1,29 @@
<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, toCurrency, toDate } from 'src/filters';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import { toCurrency, toDate } from 'src/filters';
import { useSummaryDialog } from 'composables/useSummaryDialog';
import { useState } from 'composables/useState';
import useNotify from 'composables/useNotify';
import axios from 'axios';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import RouteDescriptorProxy from 'pages/Route/Card/RouteDescriptorProxy.vue';
import InvoiceInDescriptorProxy from 'pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
import VnLv from 'components/ui/VnLv.vue';
import useNotify from 'composables/useNotify';
import RouteAutonomousFilter from 'pages/Route/Card/RouteAutonomousFilter.vue';
import { useRouter } from 'vue-router';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import { useSummaryDialog } from 'composables/useSummaryDialog';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import VnDms from 'components/common/VnDms.vue';
import { useState } from 'composables/useState';
import axios from 'axios';
import RightMenu from 'src/components/common/RightMenu.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import VnLv from 'components/ui/VnLv.vue';
const { viewSummary } = useSummaryDialog();
const { notify } = useNotify();
const { t } = useI18n();
const stateStore = useStateStore();
const { t } = useI18n();
const { notify } = useNotify();
const router = useRouter();
const { viewSummary } = useSummaryDialog();
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
const refreshKey = ref(0);
const state = useState();
const user = state.getUser();
const dmsDialog = ref({
@ -36,87 +32,89 @@ const dmsDialog = ref({
rowsToCreateInvoiceIn: [],
});
const selectedRows = ref([]);
const tableRef = ref();
const columns = computed(() => [
{
name: 'ID',
align: 'left',
name: 'id',
label: 'Id',
field: (row) => row.routeFk,
sortable: true,
align: 'left',
chip: {
condition: () => true,
},
isId: true,
},
{
name: 'date',
align: 'left',
name: 'created',
label: t('Date'),
field: (row) => toDate(row.created),
sortable: true,
align: 'left',
component: 'date',
columnFilter: {
alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
},
{
name: 'agency-route',
align: 'left',
name: 'agencyModeName',
label: t('Agency route'),
field: (row) => row?.agencyModeName,
sortable: true,
align: 'left',
cardVisible: true,
},
{
name: 'agency-agreement',
align: 'left',
name: 'agencyAgreement',
label: t('Agency agreement'),
field: (row) => row?.agencyAgreement,
sortable: true,
align: 'left',
cardVisible: true,
},
{
align: 'left',
name: 'packages',
label: t('Packages'),
field: (row) => dashIfEmpty(row.packages),
sortable: true,
align: 'left',
},
{
name: 'volume',
align: 'left',
name: 'm3',
label: 'm³',
field: (row) => dashIfEmpty(row.m3),
sortable: true,
align: 'left',
cardVisible: true,
columnFilter: {
inWhere: true,
},
},
{
name: 'km',
align: 'left',
name: 'kmTotal',
label: t('Km'),
field: (row) => dashIfEmpty(row?.kmTotal),
sortable: true,
align: 'left',
cardVisible: true,
},
{
align: 'left',
name: 'price',
label: t('Price'),
field: (row) => (row?.price ? toCurrency(row.price) : '-'),
sortable: true,
align: 'left',
},
{
name: 'received',
align: 'left',
name: 'invoiceInFk',
label: t('Received'),
field: (row) => row?.invoiceInFk,
sortable: true,
align: 'left',
},
{
name: 'autonomous',
align: 'left',
name: 'supplierName',
label: t('Autonomous'),
field: (row) => row?.supplierName,
sortable: true,
align: 'left',
},
{
name: 'actions',
label: '',
sortable: false,
align: 'right',
name: 'tableActions',
actions: [
{
title: t('Preview'),
icon: 'preview',
isPrimary: true,
action: (row) => viewSummary(row?.routeFk, RouteSummary),
},
],
},
]);
const refreshKey = ref(0);
const total = computed(() => {
return selectedRows.value.reduce((sum, item) => sum + item.price, 0);
});
@ -169,9 +167,8 @@ const onDmsSaved = async (dms, response) => {
refreshKey.value++;
};
function navigateToRouteSummary(event, row) {
router.push({ name: 'RouteSummary', params: { id: row.routeFk } });
}
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
@ -180,109 +177,63 @@ function navigateToRouteSummary(event, row) {
:label="t('Search autonomous')"
:info="t('You can search by autonomous reference')"
/>
<RightMenu>
<template #right-panel>
<RouteAutonomousFilter data-key="RouteAutonomousList" />
</template>
</RightMenu>
<QPage class="column items-center">
<div class="route-list">
<div class="q-pa-md">
<QCard class="vn-one q-py-sm q-px-lg flex justify-between">
<VnLv class="flex">
<template #label>
<span class="text-h6">{{ t('Total') }}</span>
</template>
<template #value>
<span class="text-h6 q-ml-md">{{ toCurrency(total) }}</span>
</template>
</VnLv>
<QBtn
icon="vn:invoice-in-create"
color="primary"
:disable="!selectedRows?.length"
@click="openDmsUploadDialog"
>
<QTooltip>
{{ t('Create invoiceIn') }}
</QTooltip>
</QBtn>
</QCard>
</div>
<VnPaginate
:key="refreshKey"
data-key="RouteAutonomousList"
url="AgencyTerms/filter"
:limit="20"
>
<template #body="{ rows }">
<div class="q-pa-md">
<QTable
v-model:selected="selectedRows"
:columns="columns"
:rows="rows"
flat
row-key="routeFk"
selection="multiple"
:rows-per-page-options="[0]"
hide-pagination
@row-click="navigateToRouteSummary"
>
<template #body-cell-ID="props">
<QTd :props="props">
<span class="link" @click.stop>
{{ props.value }}
<RouteDescriptorProxy :id="props.value" />
</span>
</QTd>
</template>
<template #body-cell-received="props">
<QTd :props="props">
<span :class="props.value && 'link'" @click.stop>
{{ dashIfEmpty(props.value) }}
<InvoiceInDescriptorProxy
v-if="props.value"
:id="props.value"
/>
</span>
</QTd>
</template>
<template #body-cell-autonomous="props">
<QTd :props="props">
<span class="link" @click.stop>
{{ props.value }}
<SupplierDescriptorProxy
v-if="props.row?.supplierFk"
:id="props.row?.supplierFk"
/>
</span>
</QTd>
</template>
<template #body-cell-actions="props">
<QTd :props="props">
<div class="table-actions">
<QIcon
name="preview"
size="sm"
color="primary"
@click.stop="
viewSummary(
props?.row?.routeFk,
RouteSummary
)
"
>
<QTooltip>{{ t('Preview') }}</QTooltip>
</QIcon>
</div>
</QTd>
</template>
</QTable>
</div>
<div class="q-pa-md">
<QCard class="vn-one q-py-sm q-px-lg flex justify-between">
<VnLv class="flex">
<template #label>
<span class="text-h6">{{ t('Total') }}</span>
</template>
</VnPaginate>
</div>
</QPage>
<template #value>
<span class="text-h6 q-ml-md">{{ toCurrency(total) }}</span>
</template>
</VnLv>
<QBtn
icon="vn:invoice-in-create"
color="primary"
:disable="!selectedRows?.length"
@click="openDmsUploadDialog"
>
<QTooltip>
{{ t('Create invoiceIn') }}
</QTooltip>
</QBtn>
</QCard>
</div>
<VnTable
ref="tableRef"
data-key="RouteAutonomousList"
url="AgencyTerms/filter"
:columns="columns"
:right-search="true"
default-mode="table"
v-model:selected="selectedRows"
table-height="85vh"
redirect="route"
:row-click="({ routeFk }) => tableRef.redirect(routeFk)"
:table="{
'row-key': '$index',
selection: 'multiple',
}"
>
<template #column-id="{ row }">
<span class="link" @click.stop>
{{ row.routeFk }}
<RouteDescriptorProxy :id="row.route.id" />
</span>
</template>
<template #column-invoiceInFk="{ row }">
<span class="link" @click.stop>
{{ row.invoiceInFk }}
<InvoiceInDescriptorProxy v-if="row.invoiceInFk" :id="row.invoiceInFk" />
</span>
</template>
<template #column-supplierName="{ row }">
<span class="link" @click.stop>
{{ row.supplierName }}
<SupplierDescriptorProxy :id="row.supplierFk" />
</span>
</template>
</VnTable>
<QDialog v-model="dmsDialog.show">
<VnDms
url="dms/uploadFile"
@ -292,29 +243,6 @@ function navigateToRouteSummary(event, row) {
/>
</QDialog>
</template>
<style lang="scss" scoped>
.route-list {
width: 100%;
}
.table-actions {
display: flex;
align-items: center;
gap: 12px;
i {
cursor: pointer;
}
}
th:last-child,
td:last-child {
background-color: var(--vn-section-color);
position: sticky;
right: 0;
}
</style>
<i18n>
es:
Search autonomous: Buscar autónomos

View File

@ -1,50 +1,41 @@
<script setup>
import { useArrayData } from 'composables/useArrayData';
import { dashIfEmpty, toHour } from 'src/filters';
import { computed, onMounted, ref } from 'vue';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useValidator } from 'composables/useValidator';
import { useSession } from 'composables/useSession';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useQuasar } from 'quasar';
import { toDate } from 'src/filters';
import { useRouter } from 'vue-router';
import axios from 'axios';
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
import FetchData from 'components/FetchData.vue';
import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.vue';
import { useRouter } from 'vue-router';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import { useStateStore } from 'src/stores/useStateStore';
const { t } = useI18n();
const { validate } = useValidator();
const { viewSummary } = useSummaryDialog();
const quasar = useQuasar();
const session = useSession();
const paginate = ref();
const visibleColumns = ref([]);
const selectedRows = ref([]);
const workers = ref([]);
const agencyList = ref([]);
const vehicleList = ref([]);
const allColumnNames = ref([]);
const tableRef = ref([]);
const confirmationDialog = ref(false);
const startingDate = ref(null);
const refreshKey = ref(0);
const router = useRouter();
const stateStore = useStateStore();
const routeFilter = {
include: [
{
relation: 'workers',
scope: {
fields: ['id', 'firstName'],
},
},
],
};
const columns = computed(() => [
{
align: 'left',
@ -60,75 +51,114 @@ const columns = computed(() => [
},
{
align: 'left',
name: 'worker',
name: 'workerFk',
label: t('Worker'),
create: true,
component: 'select',
attrs: {
url: 'payrollComponents',
fields: ['id', 'name'],
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'nickname'],
optionValue: 'id',
optionLabel: 'nickname',
},
useLike: false,
cardVisible: true,
format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef),
},
{
name: 'agency',
label: t('Agency'),
align: 'left',
name: 'agencyModeFk',
label: t('Agency'),
isTitle: true,
cardVisible: true,
create: true,
component: 'select',
attrs: {
url: 'agencyModes',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
},
{
name: 'vehicle',
align: 'left',
name: 'vehicleFk',
label: t('Vehicle'),
align: 'left',
cardVisible: true,
create: true,
component: 'select',
attrs: {
url: 'vehicles',
fields: ['id', 'numberPlate'],
optionLabel: 'numberPlate',
optionValue: 'id',
},
},
{
name: 'date',
align: 'left',
name: 'created',
label: t('Date'),
align: 'left',
cardVisible: true,
create: true,
component: 'date',
columnFilter: {
alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
},
{
name: 'volume',
label: 'm³',
align: 'center',
name: 'm3',
label: 'volume',
cardVisible: true,
},
{
align: 'left',
name: 'description',
label: t('Description'),
align: 'left',
isTitle: true,
create: true,
component: 'input',
field: 'description',
},
{
align: 'left',
name: 'started',
label: t('hourStarted'),
align: 'left',
component: 'time',
},
{
align: 'left',
name: 'finished',
label: t('hourFinished'),
align: 'left',
component: 'time',
},
{
name: 'isServed',
label: t('Served'),
align: 'left',
name: 'isOk',
label: t('Served'),
component: 'checkbox',
},
{
align: 'right',
label: 'asdasd',
name: 'tableActions',
actions: [
{
title: t('Client ticket list'),
title: t('Add tickets'),
icon: 'vn:ticketAdd',
action: (row) => openTicketsDialog(row?.id),
},
{
title: t('Preview'),
icon: 'preview',
action: (row) => navigate(row.id),
action: (row) => viewSummary(row?.id, RouteSummary),
},
{
title: t('Route summary'),
icon: 'arrow_forward',
isPrimary: true,
action: (row) => navigate(row?.id),
},
],
},
@ -138,22 +168,14 @@ function navigate(id) {
router.push({ path: `/route/${id}` });
}
const updateRoute = async (route) => {
try {
return await axios.patch(`Routes/${route.id}`, route);
} catch (err) {
return err;
}
};
const cloneRoutes = () => {
if (!selectedRows.value.length || !startingDate.value) return;
axios.post('Routes/clone', {
created: startingDate.value,
ids: selectedRows.value.map((row) => row?.id),
});
refreshKey.value++;
tableRef.value.reload();
startingDate.value = null;
paginate.value.fetch();
};
const showRouteReport = () => {
@ -175,16 +197,13 @@ const showRouteReport = () => {
function markAsServed() {
selectedRows.value.forEach(async (row) => {
if (row?.id) await axios.patch(`Routes/${row?.id}`, { isOk: true });
await axios.patch(`Routes/${row?.id}`, { isOk: true });
});
refreshKey.value++;
tableRef.value.reload();
startingDate.value = null;
}
const openTicketsDialog = (id) => {
if (!id) {
return;
}
quasar
.dialog({
component: RouteListTicketsDialog,
@ -215,21 +234,10 @@ const openTicketsDialog = (id) => {
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
<QBtn color="primary" v-close-popup @click="cloneRoutes">
{{ t('globals.clone') }}
<VnLv
:label="t('route.summary.packages')"
:value="getTotalPackages(entity.tickets)"
/>
</QBtn>
</QCardActions>
</QCard>
</QDialog>
<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 />
<VnSubToolbar />
<VnTable
ref="tableRef"
@ -238,13 +246,23 @@ const openTicketsDialog = (id) => {
:columns="columns"
:right-search="true"
:is-editable="true"
:filter="routeFilter"
redirect="route"
:create="{
urlCreate: 'Routes',
title: t('Create route'),
onDataSaved: () => tableRef.reload(),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}"
save-url="routes"
save-url="Routes/crud"
:disable-option="{ card: true }"
:use-model="true"
table-height="85vh"
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
>
<template #moreBeforeActions>
<QBtn

View File

@ -1,65 +1,68 @@
<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, toDateHourMin } from 'src/filters';
import { computed, ref } from 'vue';
import { dashIfEmpty } from 'src/filters';
import { toDate } from 'filters/index';
import { useQuasar } from 'quasar';
import toCurrency from 'filters/toCurrency';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import RoadmapFilter from 'pages/Route/Roadmap/RoadmapFilter.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import axios from 'axios';
import VnInputDate from 'components/common/VnInputDate.vue';
import { useSummaryDialog } from 'composables/useSummaryDialog';
import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue';
import { useRouter } from 'vue-router';
import RightMenu from 'src/components/common/RightMenu.vue';
import toCurrency from 'filters/toCurrency';
import axios from 'axios';
const stateStore = useStateStore();
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'src/components/common/VnInputTime.vue';
const { viewSummary } = useSummaryDialog();
const { t } = useI18n();
const quasar = useQuasar();
const router = useRouter();
const { viewSummary } = useSummaryDialog();
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);
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
const selectedRows = ref([]);
const columns = computed(() => [
{
name: 'roadmap',
align: 'left',
name: 'id',
label: 'Id',
isId: true,
},
{
align: 'left',
name: 'name',
label: t('Roadmap'),
field: (row) => row.name,
sortable: true,
align: 'left',
create: true,
cardVisible: true,
columnFilter: {
inWhere: true,
},
},
{
name: 'ETD',
align: 'left',
name: 'etd',
label: t('ETD'),
field: (row) => toDateHourMin(row.etd),
sortable: true,
align: 'left',
component: 'date',
columnFilter: {
alias: 'c',
inWhere: true,
},
format: ({ date }) => toDate(date),
cardVisible: true,
},
{
name: 'carrier',
align: 'left',
name: 'supplierFk',
label: t('Carrier'),
field: (row) => row.supplier?.nickname,
sortable: true,
align: 'left',
component: 'select',
attrs: {
url: 'suppliers',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
},
{
name: 'plate',
name: 'tractorPlate',
label: t('Plate'),
field: (row) => row.tractorPlate,
sortable: true,
@ -80,30 +83,29 @@ const columns = computed(() => [
align: 'left',
},
{
name: 'actions',
label: '',
sortable: false,
align: 'right',
name: 'tableActions',
actions: [
{
title: t('Ver cmr'),
icon: 'visibility',
isPrimary: true,
action: (row) => viewSummary(row?.id, RoadmapSummary),
},
],
},
]);
const refreshKey = ref(0);
const tableRef = ref(0);
const isCloneDialogOpen = ref(false);
const etdDate = ref(null);
const filter = {
include: { relation: 'supplier', scope: { fields: ['nickname'] } },
where: {
and: [{ etd: { gte: from } }, { etd: { lte: to } }],
},
};
const cloneSelection = async () => {
await axios.post('Roadmaps/clone', {
etd: etdDate.value,
ids: selectedRows.value.map((row) => row?.id),
});
refreshKey.value++;
tableRef.value.reload();
etdDate.value = null;
};
@ -125,11 +127,7 @@ function confirmRemove() {
promise: removeSelection,
},
})
.onOk(() => refreshKey.value++);
}
function navigateToRoadmapSummary(_, { id }) {
router.push({ name: 'RoadmapSummary', params: { id } });
.onOk(() => tableRef.value++);
}
</script>
@ -139,11 +137,6 @@ function navigateToRoadmapSummary(_, { id }) {
:label="t('Search roadmaps')"
:info="t('You can search by roadmap reference')"
/>
<RightMenu>
<template #right-panel>
<RoadmapFilter data-key="RoadmapList" />
</template>
</RightMenu>
<QDialog v-model="isCloneDialogOpen">
<QCard style="min-width: 350px">
<QCardSection>
@ -163,95 +156,55 @@ function navigateToRoadmapSummary(_, { id }) {
</QCardActions>
</QCard>
</QDialog>
<QPage class="column items-center">
<VnSubToolbar class="justify-end">
<template #st-actions>
<QBtn
icon="vn:clone"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="isCloneDialogOpen = true"
>
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
</QBtn>
<QBtn
icon="delete"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="confirmRemove"
>
<QTooltip>{{ t('Delete roadmap(s)') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<div class="route-list">
<VnPaginate
:key="refreshKey"
data-key="RoadmapList"
url="Roadmaps"
:limit="20"
:filter="filter"
<VnSubToolbar class="justify-end">
<template #st-actions>
<QBtn
icon="vn:clone"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="isCloneDialogOpen = true"
>
<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 }"
@row-click="navigateToRoadmapSummary"
>
<template #body-cell-carrier="props">
<QTd :props="props">
<span v-if="props.value" class="link" @click.stop>
{{ props.value }}
<SupplierDescriptorProxy
:id="props.row?.supplier?.id"
/>
</span>
</QTd>
</template>
<template #body-cell-actions="props">
<QTd :props="props">
<div class="flex items-center table-actions">
<QIcon
name="preview"
size="sm"
color="primary"
@click.stop="
viewSummary(
props?.row?.id,
RoadmapSummary
)
"
class="cursor-pointer"
>
<QTooltip>{{ t('Preview') }}</QTooltip>
</QIcon>
</div>
</QTd>
</template>
</QTable>
</div>
</template>
</VnPaginate>
</div>
<QPageSticky :offset="[20, 20]">
<RouterLink :to="{ name: 'RouteRoadmapCreate' }">
<QBtn fab icon="add" color="primary" />
<QTooltip>
{{ t('Create roadmap') }}
</QTooltip>
</RouterLink>
</QPageSticky>
</QPage>
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
</QBtn>
<QBtn
icon="delete"
color="primary"
class="q-mr-sm"
:disable="!selectedRows?.length"
@click="confirmRemove"
>
<QTooltip>{{ t('Delete roadmap(s)') }}</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<VnTable
ref="tableRef"
data-key="RoadmapList"
url="roadmaps"
:columns="columns"
:right-search="true"
:use-model="true"
default-mode="table"
v-model:selected="selectedRows"
table-height="85vh"
:table="{
selection: 'multiple',
}"
redirect="route/roadmap"
:create="{
urlCreate: 'Roadmaps',
title: t('Create routemap'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}"
:disable-option="{ card: true }"
>
<template #more-create-dialog="{ data }">
<VnInputDate v-model="data.etd" />
<VnInputTime v-model="data.etd" />
</template>
</VnTable>
</template>
<style lang="scss" scoped>
@ -264,6 +217,7 @@ function navigateToRoadmapSummary(_, { id }) {
</style>
<i18n>
es:
Create routemap: Crear troncal
Search roadmaps: Buscar troncales
You can search by roadmap reference: Puedes buscar por referencia del troncal
Delete roadmap(s): Eliminar troncal(es)

View File

@ -57,15 +57,6 @@ export default {
},
component: () => import('src/pages/Route/RouteRoadmap.vue'),
},
{
path: 'roadmap/create',
name: 'RouteRoadmapCreate',
meta: {
title: 'RouteRoadmapCreate',
icon: 'vn:troncales',
},
component: () => import('src/pages/Route/Roadmap/RoadmapCreate.vue'),
},
{
path: 'cmr',
name: 'CmrList',

View File

@ -0,0 +1,15 @@
describe('Route', () => {
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer');
cy.visit(`/#/route/list`);
});
it('Route list create route', () => {
cy.visit(`/#/route/list`);
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
cy.get('input[name="description"]').eq(1).type('routeTestOne{enter}');
cy.get('.q-notification__message').should('have.text', 'Data created');
cy.url().should('include', '/summary');
});
});