merge dev

This commit is contained in:
Pablo Natek 2024-07-03 09:33:04 +02:00
commit 61bd1eaf04
72 changed files with 1289 additions and 1113 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-front", "name": "salix-front",
"version": "24.28.1", "version": "24.30.1",
"description": "Salix frontend", "description": "Salix frontend",
"productName": "Salix", "productName": "Salix",
"author": "Verdnatura", "author": "Verdnatura",

BIN
public/no-image-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
public/no-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
public/no-user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -10,6 +10,7 @@ import VnPaginate from 'components/ui/VnPaginate.vue';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import SkeletonTable from 'components/ui/SkeletonTable.vue'; import SkeletonTable from 'components/ui/SkeletonTable.vue';
import { tMobile } from 'src/composables/tMobile'; import { tMobile } from 'src/composables/tMobile';
import VnSubToolbar from './ui/VnSubToolbar.vue';
const { push } = useRouter(); const { push } = useRouter();
const quasar = useQuasar(); const quasar = useQuasar();
@ -67,7 +68,7 @@ const $props = defineProps({
default: '', default: '',
description: 'It is used for redirect on click "save and continue"', description: 'It is used for redirect on click "save and continue"',
}, },
hasSubtoolbar: { hasSubToolbar: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
@ -313,8 +314,82 @@ watch(formUrl, async () => {
></slot> ></slot>
</template> </template>
</VnPaginate> </VnPaginate>
<SkeletonTable v-if="!formData" :columns="$attrs.columns?.length" /> <SkeletonTable
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown() && hasSubtoolbar"> v-if="!formData && $attrs.autoLoad"
:columns="$attrs.columns?.length"
/>
{{ stateStore?.isSubToolbarShown() && hasSubToolbar }}
<!-- <VnSubToolbar>
<template #st-actions>
<QBtnGroup push style="column-gap: 10px">
<slot name="moreBeforeActions" />
<QBtn
:label="tMobile('globals.remove')"
color="primary"
icon="delete"
flat
@click="remove(selected)"
:disable="!selected?.length"
:title="t('globals.remove')"
v-if="$props.defaultRemove"
/>
<QBtn
:label="tMobile('globals.reset')"
color="primary"
icon="restart_alt"
flat
@click="reset"
:disable="!hasChanges"
:title="t('globals.reset')"
v-if="$props.defaultReset"
/>
<QBtnDropdown
v-if="$props.goTo && $props.defaultSave"
@click="onSubmitAndGo"
:label="tMobile('globals.saveAndContinue')"
:title="t('globals.saveAndContinue')"
:disable="!hasChanges"
color="primary"
icon="save"
split
>
<QList>
<QItem
color="primary"
clickable
v-close-popup
@click="onSubmit"
: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-if="!$props.goTo && $props.defaultSave"
:label="tMobile('globals.save')"
ref="saveButtonRef"
color="primary"
icon="save"
@click="onSubmit"
:disable="!hasChanges"
:title="t('globals.save')"
/>
<slot name="moreAfterActions" />
</QBtnGroup>
</template>
</VnSubToolbar> -->
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown() && hasSubToolbar">
<QBtnGroup push style="column-gap: 10px"> <QBtnGroup push style="column-gap: 10px">
<slot name="moreBeforeActions" /> <slot name="moreBeforeActions" />
<QBtn <QBtn

View File

@ -90,7 +90,7 @@ const $props = defineProps({
}); });
const emit = defineEmits(['onFetch', 'onDataSaved']); const emit = defineEmits(['onFetch', 'onDataSaved']);
const modelValue = computed( const modelValue = computed(
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}` () => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`,
).value; ).value;
const componentIsRendered = ref(false); const componentIsRendered = ref(false);
const arrayData = useArrayData(modelValue); const arrayData = useArrayData(modelValue);
@ -137,7 +137,7 @@ onMounted(async () => {
JSON.stringify(newVal) !== JSON.stringify(originalData.value); JSON.stringify(newVal) !== JSON.stringify(originalData.value);
isResetting.value = false; isResetting.value = false;
}, },
{ deep: true } { deep: true },
); );
} }
}); });
@ -145,7 +145,7 @@ onMounted(async () => {
if (!$props.url) if (!$props.url)
watch( watch(
() => arrayData.store.data, () => arrayData.store.data,
(val) => updateAndEmit('onFetch', val) (val) => updateAndEmit('onFetch', val),
); );
watch(formUrl, async () => { watch(formUrl, async () => {
@ -206,11 +206,11 @@ async function save() {
updateAndEmit('onDataSaved', formData.value, response?.data); updateAndEmit('onDataSaved', formData.value, response?.data);
if ($props.reload) await arrayData.fetch({}); if ($props.reload) await arrayData.fetch({});
hasChanges.value = false;
} catch (err) { } catch (err) {
console.error(err); console.error(err);
notify('errors.writeRequest', 'negative'); notify('errors.writeRequest', 'negative');
} finally { } finally {
hasChanges.value = false;
isLoading.value = false; isLoading.value = false;
} }
} }
@ -239,7 +239,7 @@ function filter(value, update, filterOptions) {
(ref) => { (ref) => {
ref.setOptionIndex(-1); ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true); ref.moveOptionSelection(1, true);
} },
); );
} }

View File

@ -58,6 +58,7 @@ function addChildren(module, route, parent) {
} }
const items = ref([]); const items = ref([]);
function getRoutes() { function getRoutes() {
if (props.source === 'main') { if (props.source === 'main') {
const modules = Object.assign([], navigation.getModules().value); const modules = Object.assign([], navigation.getModules().value);
@ -66,9 +67,8 @@ function getRoutes() {
const moduleDef = routes.find( const moduleDef = routes.find(
(route) => toLowerCamel(route.name) === item.module (route) => toLowerCamel(route.name) === item.module
); );
item.children = [];
if (!moduleDef) continue; if (!moduleDef) continue;
item.children = [];
addChildren(item.module, moduleDef, item.children); addChildren(item.module, moduleDef, item.children);
} }

View File

@ -1,21 +1,19 @@
<script setup> <script setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useSession } from 'src/composables/useSession';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import PinnedModules from './PinnedModules.vue'; import PinnedModules from './PinnedModules.vue';
import UserPanel from 'components/UserPanel.vue'; import UserPanel from 'components/UserPanel.vue';
import VnBreadcrumbs from './common/VnBreadcrumbs.vue'; import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
import VnImg from 'src/components/ui/VnImg.vue';
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
const quasar = useQuasar(); const quasar = useQuasar();
const state = useState(); const state = useState();
const user = state.getUser(); const user = state.getUser();
const { getTokenMultimedia } = useSession();
const token = getTokenMultimedia();
const appName = 'Lilium'; const appName = 'Lilium';
onMounted(() => stateStore.setMounted()); onMounted(() => stateStore.setMounted());
@ -83,11 +81,12 @@ const pinnedModulesRef = ref();
id="user" id="user"
> >
<QAvatar size="lg"> <QAvatar size="lg">
<QImg <VnImg
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`" :id="user.id"
spinner-color="primary" collection="user"
> size="160x160"
</QImg> :zoom-size="null"
/>
</QAvatar> </QAvatar>
<QTooltip bottom> <QTooltip bottom>
{{ t('globals.userPanel') }} {{ t('globals.userPanel') }}

View File

@ -12,12 +12,14 @@ import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import { useClipboard } from 'src/composables/useClipboard'; import { useClipboard } from 'src/composables/useClipboard';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
import { useRole } from 'src/composables/useRole';
const state = useState(); const state = useState();
const session = useSession(); const session = useSession();
const router = useRouter(); const router = useRouter();
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const { copyText } = useClipboard(); const { copyText } = useClipboard();
const userLocale = computed({ const userLocale = computed({
get() { get() {
return locale.value; return locale.value;
@ -99,6 +101,7 @@ function saveUserData(param, value) {
axios.post('UserConfigs/setUserConfig', { [param]: value }); axios.post('UserConfigs/setUserConfig', { [param]: value });
localUserData(); localUserData();
} }
const isEmployee = computed(() => useRole().isEmployee());
</script> </script>
<template> <template>
@ -109,12 +112,14 @@ function saveUserData(param, value) {
auto-load auto-load
/> />
<FetchData <FetchData
v-if="isEmployee"
url="Companies" url="Companies"
order="name" order="name"
@on-fetch="(data) => (companiesData = data)" @on-fetch="(data) => (companiesData = data)"
auto-load auto-load
/> />
<FetchData <FetchData
v-if="isEmployee"
url="Accountings" url="Accountings"
order="name" order="name"
@on-fetch="(data) => (accountBankData = data)" @on-fetch="(data) => (accountBankData = data)"

View File

@ -134,7 +134,7 @@ const col = computed(() => {
const components = computed(() => $props.components ?? defaultComponents); const components = computed(() => $props.components ?? defaultComponents);
</script> </script>
<template> <template>
<div class="row no-wrap fit"> <div class="row no-wrap">
<VnComponent <VnComponent
v-if="col.before" v-if="col.before"
:prop="col.before" :prop="col.before"

View File

@ -7,6 +7,7 @@ import { useArrayData } from 'composables/useArrayData';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnTableColumn from 'components/VnTable/VnColumn.vue'; import VnTableColumn from 'components/VnTable/VnColumn.vue';
const $props = defineProps({ const $props = defineProps({
@ -75,6 +76,17 @@ const components = {
}, },
forceAttrs, forceAttrs,
}, },
time: {
component: markRaw(VnInputTime),
event: updateEvent,
attrs: {
...defaultAttrs,
disable: !$props.isEditable,
},
forceAttrs: {
label: $props.showLabel && $props.column.label,
},
},
checkbox: { checkbox: {
component: markRaw(QCheckbox), component: markRaw(QCheckbox),
event: updateEvent, event: updateEvent,

View File

@ -59,6 +59,14 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
hasSubToolbar: {
type: Boolean,
default: true,
},
disableOption: {
type: Object,
default: () => ({ card: false, table: false }),
},
}); });
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -80,11 +88,13 @@ const tableModes = [
icon: 'view_column', icon: 'view_column',
title: t('table view'), title: t('table view'),
value: TABLE_MODE, value: TABLE_MODE,
disable: $props.disableOption?.table,
}, },
{ {
icon: 'grid_view', icon: 'grid_view',
title: t('grid view'), title: t('grid view'),
value: DEFAULT_MODE, value: DEFAULT_MODE,
disable: $props.disableOption?.card,
}, },
]; ];
@ -175,11 +185,14 @@ function columnName(col) {
} }
function getColAlign(col) { function getColAlign(col) {
return 'text-' + (col.align ?? 'left') return 'text-' + (col.align ?? 'left');
} }
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
defineExpose({ defineExpose({
reload, reload,
redirect: redirectFn, redirect: redirectFn,
selected,
}); });
</script> </script>
<template> <template>
@ -225,11 +238,18 @@ defineExpose({
:search-url="searchUrl" :search-url="searchUrl"
:disable-infinite-scroll="mode == TABLE_MODE" :disable-infinite-scroll="mode == TABLE_MODE"
@save-changes="reload" @save-changes="reload"
:has-subtoolbar="isEditable" :has-sub-toolbar="$attrs['hasSubToolbar'] ?? isEditable"
> >
<template
v-for="(_, slotName) in $slots"
#[slotName]="slotData"
:key="slotName"
>
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
</template>
<template #body="{ rows }"> <template #body="{ rows }">
<QTable <QTable
v-bind="$attrs['QTable']" v-bind="$attrs['q-table']"
class="vnTable" class="vnTable"
:columns="splittedColumns.columns" :columns="splittedColumns.columns"
:rows="rows" :rows="rows"
@ -246,6 +266,7 @@ defineExpose({
CrudModelRef.vnPaginateRef.paginate() CrudModelRef.vnPaginateRef.paginate()
" "
@row-click="(_, row) => rowClickFunction(row)" @row-click="(_, row) => rowClickFunction(row)"
@update:selected="emit('update:selected', $event)"
> >
<template #top-left> <template #top-left>
<slot name="top-left"></slot> <slot name="top-left"></slot>
@ -310,13 +331,15 @@ defineExpose({
class="no-margin q-px-xs" class="no-margin q-px-xs"
:class="getColAlign(col)" :class="getColAlign(col)"
> >
<VnTableColumn <slot :name="`column-${col.name}`" :col="col" :row="row">
:column="col" <VnTableColumn
:row="row" :column="col"
:is-editable="false" :row="row"
v-model="row[col.name]" :is-editable="false"
component-prop="columnField" v-model="row[col.name]"
/> component-prop="columnField"
/>
</slot>
</QTd> </QTd>
</template> </template>
<template #body-cell-tableActions="{ col, row }"> <template #body-cell-tableActions="{ col, row }">
@ -401,9 +424,9 @@ defineExpose({
> >
<VnLv <VnLv
:label=" :label="
!col.component && !col.component && col.label
col.label && ? `${col.label}:`
`${col.label}:` : ''
" "
> >
<template #value> <template #value>
@ -412,14 +435,20 @@ defineExpose({
stopEventPropagation($event) stopEventPropagation($event)
" "
> >
<VnTableColumn <slot
:column="col" :name="`column-${col.name}`"
:col="col"
:row="row" :row="row"
:is-editable="false" >
v-model="row[col.name]" <VnTableColumn
component-prop="columnField" :column="col"
:show-label="true" :row="row"
/> :is-editable="false"
v-model="row[col.name]"
component-prop="columnField"
:show-label="true"
/>
</slot>
</span> </span>
</template> </template>
</VnLv> </VnLv>
@ -477,6 +506,7 @@ defineExpose({
default="input" default="input"
v-model="data[column.name]" v-model="data[column.name]"
:show-label="true" :show-label="true"
component-prop="columnCreate"
/> />
<slot name="more-create-dialog" :data="data" /> <slot name="more-create-dialog" :data="data" />
</div> </div>

View File

@ -12,7 +12,7 @@ const $props = defineProps({
default: () => {}, default: () => {},
}, },
value: { value: {
type: [Object, Number, String], type: [Object, Number, String, Boolean],
default: () => {}, default: () => {},
}, },
}); });

View File

@ -93,7 +93,12 @@ const inputRules = [
name="close" name="close"
size="xs" size="xs"
v-if="hover && value && !$attrs.disabled && $props.clearable" v-if="hover && value && !$attrs.disabled && $props.clearable"
@click="value = null" @click="
() => {
value = null;
emit('remove');
}
"
></QIcon> ></QIcon>
<QIcon v-if="info" name="info"> <QIcon v-if="info" name="info">
<QTooltip max-width="350px"> <QTooltip max-width="350px">

View File

@ -1,84 +1,31 @@
<script setup> <script setup>
import { computed, ref } from 'vue'; import { onMounted, watch, computed, ref } from 'vue';
import { date } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import isValidDate from 'filters/isValidDate';
const props = defineProps({ const model = defineModel({ type: String });
modelValue: { const $props = defineProps({
type: String,
default: null,
},
readonly: {
type: Boolean,
default: false,
},
isOutlined: { isOutlined: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
emitDateFormat: {
type: Boolean,
default: false,
},
}); });
const hover = ref(false);
const emit = defineEmits(['update:modelValue']);
const { t } = useI18n(); const { t } = useI18n();
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired'); const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
const joinDateAndTime = (date, time) => { const dateFormat = 'DD/MM/YYYY';
if (!date) { const isPopupOpen = ref();
return null; const hover = ref();
} const mask = ref();
if (!time) {
return new Date(date).toISOString();
}
const [year, month, day] = date.split('/');
return new Date(`${year}-${month}-${day}T${time}`).toISOString();
};
const time = computed(() => (props.modelValue ? props.modelValue.split('T')?.[1] : null)); onMounted(() => {
const value = computed({ // fix quasar bug
get() { mask.value = '##/##/####';
return props.modelValue;
},
set(value) {
emit(
'update:modelValue',
props.emitDateFormat ? new Date(value) : joinDateAndTime(value, time.value)
);
},
}); });
const isPopupOpen = ref(false);
const onDateUpdate = (date) => {
value.value = date;
isPopupOpen.value = false;
};
const padDate = (value) => value.toString().padStart(2, '0');
const formatDate = (dateString) => {
const date = new Date(dateString || '');
return `${date.getFullYear()}/${padDate(date.getMonth() + 1)}/${padDate(
date.getDate()
)}`;
};
const displayDate = (dateString) => {
if (!dateString || !isValidDate(dateString)) {
return '';
}
return new Date(dateString).toLocaleDateString([], {
year: 'numeric',
month: '2-digit',
day: '2-digit',
});
};
const styleAttrs = computed(() => { const styleAttrs = computed(() => {
return props.isOutlined return $props.isOutlined
? { ? {
dense: true, dense: true,
outlined: true, outlined: true,
@ -86,52 +33,101 @@ const styleAttrs = computed(() => {
} }
: {}; : {};
}); });
const formattedDate = computed({
get() {
if (!model.value) return model.value;
return date.formatDate(new Date(model.value), dateFormat);
},
set(value) {
if (value == model.value) return;
let newDate;
if (value) {
// parse input
if (value.includes('/') && value.length >= 10) {
if (value.at(2) == '/') value = value.split('/').reverse().join('/');
value = date.formatDate(
new Date(value).toISOString(),
'YYYY-MM-DDTHH:mm:ss.SSSZ'
);
}
let ymd = value.split('-').map((e) => parseInt(e));
newDate = new Date(ymd[0], ymd[1] - 1, ymd[2]);
if (model.value) {
const orgDate =
model.value instanceof Date ? model.value : new Date(model.value);
newDate.setHours(
orgDate.getHours(),
orgDate.getMinutes(),
orgDate.getSeconds(),
orgDate.getMilliseconds()
);
}
}
if (!isNaN(newDate)) model.value = newDate.toISOString();
},
});
const popupDate = computed(() =>
model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value
);
watch(
() => model.value,
(val) => (formattedDate.value = val),
{ immediate: true }
);
</script> </script>
<template> <template>
<div @mouseover="hover = true" @mouseleave="hover = false"> <div @mouseover="hover = true" @mouseleave="hover = false">
<QInput <QInput
v-model="formattedDate"
class="vn-input-date" class="vn-input-date"
readonly :mask="mask"
:model-value="displayDate(value)" placeholder="dd/mm/aaaa"
v-bind="{ ...$attrs, ...styleAttrs }" v-bind="{ ...$attrs, ...styleAttrs }"
:class="{ required: $attrs.required }" :class="{ required: $attrs.required }"
:rules="$attrs.required ? [requiredFieldRule] : null" :rules="$attrs.required ? [requiredFieldRule] : null"
@click="isPopupOpen = true" :clearable="false"
> >
<template #append> <template #append>
<QIcon <QIcon
name="close" name="close"
size="xs" size="xs"
v-if="hover && value && !readonly" v-if="
@click="onDateUpdate(null)" ($attrs.clearable == undefined || $attrs.clearable) &&
></QIcon> hover &&
<QIcon name="event" class="cursor-pointer"> model &&
<QPopupProxy !$attrs.disable
v-model="isPopupOpen" "
cover @click="
transition-show="scale" model = null;
transition-hide="scale" isPopupOpen = false;
:no-parent-event="props.readonly" "
> />
<QDate <QIcon name="event" class="cursor-pointer" />
:today-btn="true"
:model-value="formatDate(value)"
@update:model-value="onDateUpdate"
/>
</QPopupProxy>
</QIcon>
</template> </template>
<QMenu
transition-show="scale"
transition-hide="scale"
v-model="isPopupOpen"
anchor="bottom left"
self="top start"
:no-focus="true"
>
<QDate
v-model="popupDate"
:today-btn="true"
@update:model-value="
(date) => {
formattedDate = date;
isPopupOpen = false;
}
"
/>
</QMenu>
</QInput> </QInput>
</div> </div>
</template> </template>
<style lang="scss">
.vn-input-date.q-field--standard.q-field--readonly .q-field__control:before {
border-bottom-style: solid;
}
.vn-input-date.q-field--outlined.q-field--readonly .q-field__control:before {
border-style: solid;
}
</style>

View File

@ -1,14 +1,11 @@
<script setup> <script setup>
import { computed, ref } from 'vue'; import { watch, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import isValidDate from 'filters/isValidDate'; import { date } from 'quasar';
const model = defineModel({ type: String });
const props = defineProps({ const props = defineProps({
modelValue: { timeOnly: {
type: String,
default: null,
},
readonly: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
@ -17,43 +14,12 @@ const props = defineProps({
default: false, default: false,
}, },
}); });
const emit = defineEmits(['update:modelValue']);
const { t } = useI18n(); const { t } = useI18n();
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired'); const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
const value = computed({ const dateFormat = 'HH:mm';
get() { const isPopupOpen = ref();
return props.modelValue; const hover = ref();
},
set(value) {
const [hours, minutes] = value.split(':');
const date = new Date(props.modelValue);
date.setHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0);
emit('update:modelValue', value ? date.toISOString() : null);
},
});
const isPopupOpen = ref(false);
const onDateUpdate = (date) => {
internalValue.value = date;
};
const save = () => {
value.value = internalValue.value;
};
const formatTime = (dateString) => {
if (!dateString || !isValidDate(dateString)) {
return '';
}
const date = new Date(dateString || '');
return date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
});
};
const internalValue = ref(formatTime(value));
const styleAttrs = computed(() => { const styleAttrs = computed(() => {
return props.isOutlined return props.isOutlined
@ -64,65 +30,84 @@ const styleAttrs = computed(() => {
} }
: {}; : {};
}); });
const formattedTime = computed({
get() {
if (!model.value || model.value?.length <= 5) return model.value;
return dateToTime(model.value);
},
set(value) {
if (value == model.value) return;
let time = value;
if (time) {
if (time?.length > 5) time = dateToTime(time);
if (!props.timeOnly) {
const hours = time.split(':');
const date = new Date();
date.setHours(hours[0], hours[1], 0);
time = date.toISOString();
}
}
model.value = time;
},
});
function dateToTime(newDate) {
return date.formatDate(new Date(newDate), dateFormat);
}
watch(
() => model.value,
(val) => (formattedTime.value = val),
{ immediate: true }
);
</script> </script>
<template> <template>
<QInput <div @mouseover="hover = true" @mouseleave="hover = false">
class="vn-input-time" <QInput
readonly class="vn-input-time"
:model-value="formatTime(value)" mask="##:##"
v-bind="{ ...$attrs, ...styleAttrs }" placeholder="--:--"
:class="{ required: $attrs.required }" v-model="formattedTime"
:rules="$attrs.required ? [requiredFieldRule] : null" v-bind="{ ...$attrs, ...styleAttrs }"
@click="isPopupOpen = true" :class="{ required: $attrs.required }"
> style="min-width: 100px"
<template #append> :rules="$attrs.required ? [requiredFieldRule] : null"
<QIcon name="Schedule" class="cursor-pointer"> >
<QPopupProxy <template #append>
v-model="isPopupOpen" <QIcon
cover name="close"
transition-show="scale" size="xs"
transition-hide="scale" v-if="
:no-parent-event="props.readonly" ($attrs.clearable == undefined || $attrs.clearable) &&
> hover &&
<QTime model &&
:format24h="false" !$attrs.disable
:model-value="formatTime(value)" "
@update:model-value="onDateUpdate" @click="
> model = null;
<div class="row items-center justify-end q-gutter-sm"> isPopupOpen = false;
<QBtn "
:label="t('Cancel')" />
color="primary" <QIcon name="Schedule" class="cursor-pointer" />
flat </template>
v-close-popup <QMenu
/> transition-show="scale"
<QBtn transition-hide="scale"
label="Ok" v-model="isPopupOpen"
color="primary" anchor="bottom left"
flat self="top start"
@click="save" :no-focus="true"
v-close-popup >
/> <QTime
</div> :format24h="false"
</QTime> v-model="formattedTime"
</QPopupProxy> mask="HH:mm"
</QIcon> landscape
</template> now-btn
</QInput> />
</QMenu>
</QInput>
</div>
</template> </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

@ -61,6 +61,10 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
useLike: {
type: Boolean,
default: true,
},
}); });
const { t } = useI18n(); const { t } = useI18n();
@ -114,11 +118,14 @@ async function fetchFilter(val) {
if (!$props.url || !dataRef.value) return; if (!$props.url || !dataRef.value) return;
const { fields, sortBy, limit } = $props; const { fields, sortBy, limit } = $props;
let key = optionLabel.value; let key = optionFilter.value ?? optionLabel.value;
if (new RegExp(/\d/g).test(val)) key = optionFilter.value ?? optionValue.value; if (new RegExp(/\d/g).test(val)) key = optionValue.value;
const where = { ...{ [key]: { like: `%${val}%` } }, ...$props.where }; const defaultWhere = $props.useLike
? { [key]: { like: `%${val}%` } }
: { [key]: val };
const where = { ...defaultWhere, ...$props.where };
const fetchOptions = { where, order: sortBy, limit }; const fetchOptions = { where, order: sortBy, limit };
if (fields) fetchOptions.fields = fields; if (fields) fetchOptions.fields = fields;
return dataRef.value.fetch(fetchOptions); return dataRef.value.fetch(fetchOptions);

View File

@ -10,7 +10,7 @@ const { t } = useI18n();
const $props = defineProps({ const $props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: () => {} default: () => {},
}, },
dataKey: { dataKey: {
type: String, type: String,
@ -58,7 +58,14 @@ const $props = defineProps({
}, },
}); });
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']); const emit = defineEmits([
'update:modelValue',
'refresh',
'clear',
'search',
'init',
'remove',
]);
const arrayData = useArrayData($props.dataKey, { const arrayData = useArrayData($props.dataKey, {
exprBuilder: $props.exprBuilder, exprBuilder: $props.exprBuilder,
@ -67,9 +74,9 @@ const arrayData = useArrayData($props.dataKey, {
}); });
const route = useRoute(); const route = useRoute();
const store = arrayData.store; const store = arrayData.store;
const userParams = ref({}) const userParams = ref({});
onMounted(() => { onMounted(() => {
userParams.value = $props.modelValue ?? {} userParams.value = $props.modelValue ?? {};
emit('init', { params: userParams.value }); emit('init', { params: userParams.value });
}); });
@ -92,6 +99,11 @@ watch(
(val) => setUserParams(val) (val) => setUserParams(val)
); );
watch(
() => $props.modelValue,
(val) => (userParams.value = val ?? {})
);
const isLoading = ref(false); const isLoading = ref(false);
async function search(evt) { async function search(evt) {
if (evt && $props.disableSubmitEvent) return; if (evt && $props.disableSubmitEvent) return;
@ -145,6 +157,7 @@ async function clearFilters() {
isLoading.value = false; isLoading.value = false;
emit('clear'); emit('clear');
emit('update:modelValue', userParams.value);
} }
const tagsList = computed(() => { const tagsList = computed(() => {
@ -168,6 +181,7 @@ async function remove(key) {
userParams.value[key] = undefined; userParams.value[key] = undefined;
search(); search();
emit('remove', key); emit('remove', key);
emit('update:modelValue', userParams.value);
} }
function formatValue(value) { function formatValue(value) {

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed } from 'vue';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
const $props = defineProps({ const $props = defineProps({
@ -28,21 +28,29 @@ const $props = defineProps({
const show = ref(false); const show = ref(false);
const token = useSession().getTokenMultimedia(); const token = useSession().getTokenMultimedia();
const timeStamp = ref(`timestamp=${Date.now()}`); const timeStamp = ref(`timestamp=${Date.now()}`);
const url = computed( import noImage from '/public/no-user.png';
() => import { useRole } from 'src/composables/useRole';
`/api/${$props.storage}/${$props.collection}/${$props.size}/${$props.id}/download?access_token=${token}&${timeStamp.value}` const url = computed(() => {
); const isEmployee = useRole().isEmployee();
return isEmployee
? `/api/${$props.storage}/${$props.collection}/${$props.size}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
: noImage;
});
const reload = () => { const reload = () => {
timeStamp.value = `timestamp=${Date.now()}`; timeStamp.value = `timestamp=${Date.now()}`;
}; };
defineExpose({ defineExpose({
reload, reload,
}); });
onMounted(() => {});
</script> </script>
<template> <template>
<QImg :src="url" v-bind="$attrs" @click="show = !show" spinner-color="primary" /> <QImg
:class="{ zoomIn: $props.zoomSize }"
:src="url"
v-bind="$attrs"
@click="show = !show"
spinner-color="primary"
/>
<QDialog v-model="show" v-if="$props.zoomSize"> <QDialog v-model="show" v-if="$props.zoomSize">
<QImg <QImg
:src="url" :src="url"
@ -56,7 +64,9 @@ onMounted(() => {});
<style lang="scss" scoped> <style lang="scss" scoped>
.q-img { .q-img {
cursor: zoom-in; &.zoomIn {
cursor: zoom-in;
}
min-width: 50px; min-width: 50px;
} }
.rounded { .rounded {

View File

@ -1,6 +1,8 @@
<script setup> <script setup>
import { onMounted, onUnmounted, ref } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { onBeforeRouteLeave } from 'vue-router';
const stateStore = useStateStore(); const stateStore = useStateStore();
const actions = ref(null); const actions = ref(null);
const data = ref(null); const data = ref(null);
@ -27,6 +29,7 @@ onMounted(() => {
onUnmounted(() => { onUnmounted(() => {
stateStore.toggleSubToolbar(); stateStore.toggleSubToolbar();
}); });
onBeforeRouteLeave((to, from) => {});
</script> </script>
<template> <template>

View File

@ -27,8 +27,12 @@ export function useRole() {
return false; return false;
} }
function isEmployee() {
return hasAny(['employee']);
}
return { return {
isEmployee,
fetch, fetch,
hasAny, hasAny,
state, state,

View File

@ -100,13 +100,143 @@ globals:
modes: Modes modes: Modes
zones: Zones zones: Zones
zonesList: Zones zonesList: Zones
deliveryList: Delivery days deliveryDays: Delivery days
upcomingList: Upcoming deliveries upcomingDeliveries: Upcoming deliveries
role: Role role: Role
alias: Alias alias: Alias
aliasUsers: Users aliasUsers: Users
subRoles: Subroles subRoles: Subroles
inheritedRoles: Inherited Roles inheritedRoles: Inherited Roles
customers: Customers
list: List
webPayments: Web Payments
extendedList: Extended list
notifications: Notifications
defaulter: Defaulter
customerCreate: New customer
fiscalData: Fiscal data
billingData: Billing data
consignees: Consignees
notes: Notes
credits: Credits
greuges: Greuges
balance: Balance
recoveries: Recoveries
webAccess: Web access
sms: Sms
creditManagement: Credit management
creditContracts: Credit contracts
creditOpinion: Credit opinion
others: Others
samples: Samples
consumption: Consumption
mandates: Mandates
contacts: Contacts
webPayment: Web payment
fileManagement: File management
unpaid: Unpaid
entries: Entries
buys: Buys
dms: File management
entryCreate: New entry
latestBuys: Latest buys
tickets: Tickets
ticketCreate: New Tickets
boxing: Boxing
sale: Sale
claims: Claims
claimCreate: New claim
lines: Lines
photos: Photos
development: Development
action: Action
invoiceOuts: Invoice out
negativeBases: Negative Bases
globalInvoicing: Global invoicing
invoiceOutCreate: Create invoice out
shelving: Shelving
shelvingList: Shelving List
shelvingCreate: New shelving
invoiceIns: Invoices In
invoiceInCreate: Create invoice in
vat: VAT
dueDay: Due day
intrastat: Intrastat
corrective: Corrective
order: Orders
orderList: List
orderCreate: New order
catalog: Catalog
volume: Volume
workers: Workers
workerCreate: New worker
department: Department
pda: PDA
pbx: Private Branch Exchange
calendar: Calendar
timeControl: Time control
locker: Locker
wagons: Wagons
wagonsList: Wagons List
wagonCreate: Create wagon
wagonEdit: Edit wagon
typesList: Types List
typeCreate: Create type
typeEdit: Edit type
wagonCounter: Trolley counter
roadmap: Roadmap
stops: Stops
routes: Routes
cmrsList: CMRs list
RouteList: List
routeCreate: New route
RouteRoadmap: Roadmaps
RouteRoadmapCreate: Create roadmap
autonomous: Autonomous
suppliers: Suppliers
supplier: Supplier
labeler: Labeler
supplierCreate: New supplier
accounts: Accounts
addresses: Addresses
agencyTerm: Agency agreement
travel: Travels
extraCommunity: Extra community
travelCreate: New travel
history: Log
thermographs: Thermograph
items: Items
diary: Diary
tags: Tags
create: Create
buyRequest: Buy requests
fixedPrice: Fixed prices
wasteBreakdown: Waste breakdown
itemCreate: New item
barcode: Barcodes
tax: Tax
botanical: Botanical
itemTypeCreate: New item type
family: Item Type
lastEntries: Last entries
itemType: Item type
monitors: Monitors
dashboard: Dashboard
users: Users
createTicket: Create ticket
ticketAdvance: Advance tickets
futureTickets: Future tickets
purchaseRequest: Purchase request
weeklyTickets: Weekly tickets
formation: Formation
locations: Locations
warehouses: Warehouses
roles: Roles
connections: Connections
acls: ACLs
mailForwarding: Mail forwarding
mailAlias: Mail alias
privileges: Privileges
created: Created created: Created
worker: Worker worker: Worker
now: Now now: Now
@ -148,40 +278,8 @@ verifyEmail:
verifyEmail: Email verification verifyEmail: Email verification
dashboard: dashboard:
pageTitles: pageTitles:
dashboard: Dashboard
customer: customer:
pageTitles:
customers: Customers
list: List
webPayments: Web Payments
extendedList: Extended list
notifications: Notifications
defaulter: Defaulter
customerCreate: New customer
summary: Summary
basicData: Basic data
fiscalData: Fiscal data
billingData: Billing data
consignees: Consignees
notes: Notes
credits: Credits
greuges: Greuges
balance: Balance
recoveries: Recoveries
webAccess: Web access
log: Log
sms: Sms
creditManagement: Credit management
creditContracts: Credit contracts
creditOpinion: Credit opinion
others: Others
samples: Samples
consumption: Consumption
mandates: Mandates
contacts: Contacts
webPayment: Web payment
fileManagement: File management
unpaid: Unpaid
list: list:
phone: Phone phone: Phone
email: Email email: Email
@ -311,17 +409,6 @@ customer:
hasCoreVnl: VNL core received hasCoreVnl: VNL core received
hasSepaVnl: VNL B2B received hasSepaVnl: VNL B2B received
entry: entry:
pageTitles:
entries: Entries
list: List
summary: Summary
basicData: Basic data
buys: Buys
notes: Notes
dms: File management
log: Log
entryCreate: New entry
latestBuys: Latest buys
list: list:
newEntry: New entry newEntry: New entry
landed: Landed landed: Landed
@ -526,18 +613,6 @@ ticket:
warehouse: Warehouse warehouse: Warehouse
agency: Agency agency: Agency
claim: claim:
pageTitles:
claims: Claims
list: List
claimCreate: New claim
summary: Summary
basicData: Basic Data
lines: Lines
photos: Photos
development: Development
log: Audit logs
notes: Notes
action: Action
list: list:
customer: Customer customer: Customer
assignedTo: Assigned assignedTo: Assigned
@ -601,14 +676,6 @@ claim:
noData: 'There are no images/videos, click here or drag and drop the file' noData: 'There are no images/videos, click here or drag and drop the file'
dragDrop: Drag and drop it here dragDrop: Drag and drop it here
invoiceOut: invoiceOut:
pageTitles:
invoiceOuts: Invoice out
list: List
negativeBases: Negative Bases
globalInvoicing: Global invoicing
invoiceOutCreate: Create invoice out
summary: Summary
basicData: Basic Data
list: list:
ref: Reference ref: Reference
issued: Issued issued: Issued
@ -676,13 +743,6 @@ invoiceOut:
errors: errors:
downloadCsvFailed: CSV download failed downloadCsvFailed: CSV download failed
shelving: shelving:
pageTitles:
shelving: Shelving
shelvingList: Shelving List
shelvingCreate: New shelving
summary: Summary
basicData: Basic Data
log: Logs
list: list:
parking: Parking parking: Parking
priority: Priority priority: Priority
@ -709,17 +769,6 @@ parking:
info: You can search by parking code info: You can search by parking code
label: Search parking... label: Search parking...
invoiceIn: invoiceIn:
pageTitles:
invoiceIns: Invoices In
list: List
invoiceInCreate: Create invoice in
summary: Summary
basicData: Basic data
vat: VAT
dueDay: Due day
intrastat: Intrastat
corrective: Corrective
log: Logs
list: list:
ref: Reference ref: Reference
supplier: Supplier supplier: Supplier
@ -770,15 +819,6 @@ invoiceIn:
stems: Stems stems: Stems
country: Country country: Country
order: order:
pageTitles:
order: Orders
orderList: List
orderCreate: New order
summary: Summary
basicData: Basic Data
catalog: Catalog
volume: Volume
lines: Lines
field: field:
salesPersonFk: Sales Person salesPersonFk: Sales Person
clientFk: Client clientFk: Client
@ -854,7 +894,6 @@ worker:
timeControl: Time control timeControl: Time control
locker: Locker locker: Locker
balance: Balance balance: Balance
formation: Formation
list: list:
name: Name name: Name
email: Email email: Email
@ -943,15 +982,6 @@ worker:
credit: Have credit: Have
concept: Concept concept: Concept
wagon: wagon:
pageTitles:
wagons: Wagons
wagonsList: Wagons List
wagonCreate: Create wagon
wagonEdit: Edit wagon
typesList: Types List
typeCreate: Create type
typeEdit: Edit type
wagonCounter: Trolley counter
type: type:
name: Name name: Name
submit: Submit submit: Submit
@ -980,18 +1010,6 @@ wagon:
minHeightBetweenTrays: 'The minimum height between trays is ' minHeightBetweenTrays: 'The minimum height between trays is '
maxWagonHeight: 'The maximum height of the wagon is ' maxWagonHeight: 'The maximum height of the wagon is '
uncompleteTrays: There are incomplete trays uncompleteTrays: There are incomplete trays
route/roadmap:
pageTitles:
roadmap: Roadmap
summary: Summary
basicData: Basic Data
stops: Stops
roadmap:
pageTitles:
roadmap: Roadmap
summary: Summary
basicData: Basic Data
stops: Stops
route: route:
pageTitles: pageTitles:
agency: Agency List agency: Agency List
@ -1033,22 +1051,6 @@ route:
volume: Volume volume: Volume
finished: Finished finished: Finished
supplier: supplier:
pageTitles:
suppliers: Suppliers
supplier: Supplier
list: List
supplierCreate: New supplier
summary: Summary
basicData: Basic data
fiscalData: Fiscal data
billingData: Billing data
log: Log
accounts: Accounts
contacts: Contacts
addresses: Addresses
consumption: Consumption
agencyTerm: Agency agreement
dms: File management
list: list:
payMethod: Pay method payMethod: Pay method
payDeadline: Pay deadline payDeadline: Pay deadline
@ -1140,15 +1142,6 @@ supplier:
date: Date date: Date
reference: Reference reference: Reference
travel: travel:
pageTitles:
travel: Travels
list: List
summary: Summary
extraCommunity: Extra community
travelCreate: New travel
basicData: Basic data
history: Log
thermographs: Thermograph
summary: summary:
confirmed: Confirmed confirmed: Confirmed
entryId: Entry Id entryId: Entry Id
@ -1195,24 +1188,6 @@ travel:
travelFileDescription: 'Travel id { travelId }' travelFileDescription: 'Travel id { travelId }'
file: File file: File
item: item:
pageTitles:
items: Items
list: List
diary: Diary
tags: Tags
create: Create
buyRequest: Buy requests
fixedPrice: Fixed prices
wasteBreakdown: Waste breakdown
itemCreate: New item
barcode: Barcodes
tax: Tax
log: Log
botanical: Botanical
shelving: Shelving
itemTypeCreate: New item type
family: Item Type
lastEntries: Last entries
descriptor: descriptor:
item: Item item: Item
buyer: Buyer buyer: Buyer
@ -1298,22 +1273,6 @@ item:
minSalesQuantity: 'Cantidad mínima de venta' minSalesQuantity: 'Cantidad mínima de venta'
genus: 'Genus' genus: 'Genus'
specie: 'Specie' specie: 'Specie'
item/itemType:
pageTitles:
itemType: Item type
basicData: Basic data
summary: Summary
monitor:
pageTitles:
monitors: Monitors
list: List
zone:
pageTitles:
zones: Zones
zonesList: Zones
deliveryList: Delivery days
upcomingList: Upcoming deliveries
components: components:
topbar: {} topbar: {}
itemsFilterPanel: itemsFilterPanel:

View File

@ -100,14 +100,144 @@ globals:
modes: Modos modes: Modos
zones: Zonas zones: Zonas
zonesList: Zonas zonesList: Zonas
deliveryList: Días de entrega deliveryDays: Días de entrega
upcomingList: Próximos repartos upcomingDeliveries: Próximos repartos
role: Role role: Role
alias: Alias alias: Alias
aliasUsers: Usuarios aliasUsers: Usuarios
subRoles: Subroles subRoles: Subroles
inheritedRoles: Roles heredados inheritedRoles: Roles heredados
customers: Clientes
customerCreate: Nuevo cliente
list: Listado
webPayments: Pagos Web
extendedList: Listado extendido
notifications: Notificaciones
defaulter: Morosos
createCustomer: Crear cliente
fiscalData: Datos fiscales
billingData: Forma de pago
consignees: Consignatarios
notes: Notas
credits: Créditos
greuges: Greuges
balance: Balance
recoveries: Recobros
webAccess: Acceso web
sms: Sms
creditManagement: Gestión de crédito
creditContracts: Contratos de crédito
creditOpinion: Opinión de crédito
others: Otros
samples: Plantillas
consumption: Consumo
mandates: Mandatos
contacts: Contactos
webPayment: Pago web
fileManagement: Gestión documental
unpaid: Impago
entries: Entradas
buys: Compras
dms: Gestión documental
entryCreate: Nueva entrada
latestBuys: Últimas compras
tickets: Tickets
ticketCreate: Nuevo ticket
boxing: Encajado
sale: Lineas del pedido
claims: Reclamaciones
claimCreate: Crear reclamación
lines: Líneas
development: Trazabilidad
photos: Fotos
action: Acción
invoiceOuts: Fact. emitidas
negativeBases: Bases Negativas
globalInvoicing: Facturación global
invoiceOutCreate: Crear fact. emitida
order: Cesta
orderList: Listado
orderCreate: Nueva orden
catalog: Catálogo
volume: Volumen
shelving: Carros
shelvingList: Listado de carros
shelvingCreate: Nuevo carro
invoiceIns: Fact. recibidas
invoiceInCreate: Crear fact. recibida
vat: IVA
labeler: Etiquetas
dueDay: Vencimiento
intrastat: Intrastat
corrective: Rectificativa
workers: Trabajadores workers: Trabajadores
workerCreate: Nuevo trabajador
department: Departamentos
pda: PDA
pbx: Centralita
calendar: Calendario
timeControl: Control de horario
locker: Taquilla
wagons: Vagones
wagonsList: Listado vagones
wagonCreate: Crear tipo
wagonEdit: Editar tipo
typesList: Listado tipos
typeCreate: Crear tipo
typeEdit: Editar tipo
wagonCounter: Contador de carros
roadmap: Troncales
stops: Paradas
routes: Rutas
cmrsList: Listado de CMRs
RouteList: Listado
routeCreate: Nueva ruta
RouteRoadmap: Troncales
RouteRoadmapCreate: Crear troncal
autonomous: Autónomos
suppliers: Proveedores
supplier: Proveedor
supplierCreate: Nuevo proveedor
accounts: Cuentas
addresses: Direcciones
agencyTerm: Acuerdo agencia
travel: Envíos
create: Crear
extraCommunity: Extra comunitarios
travelCreate: Nuevo envío
history: Historial
thermographs: Termógrafos
items: Artículos
diary: Histórico
tags: Etiquetas
fixedPrice: Precios fijados
buyRequest: Peticiones de compra
wasteBreakdown: Deglose de mermas
itemCreate: Nuevo artículo
tax: 'IVA'
botanical: 'Botánico'
barcode: 'Código de barras'
itemTypeCreate: Nueva familia
family: Familia
lastEntries: Últimas entradas
itemType: Familia
monitors: Monitores
dashboard: Tablón
users: Usuarios
createTicket: Crear ticket
ticketAdvance: Adelantar tickets
futureTickets: Tickets a futuro
purchaseRequest: Petición de compra
weeklyTickets: Tickets programados
formation: Formación
locations: Ubicaciones
warehouses: Almacenes
roles: Roles
connections: Conexiones
acls: ACLs
mailForwarding: Reenvío de correo
mailAlias: Alias de correo
privileges: Privilegios
created: Fecha creación created: Fecha creación
worker: Trabajador worker: Trabajador
now: Ahora now: Ahora
@ -147,41 +277,8 @@ verifyEmail:
verifyEmail: Verificación de correo verifyEmail: Verificación de correo
dashboard: dashboard:
pageTitles: pageTitles:
dashboard: Tablón
customer: customer:
pageTitles:
customers: Clientes
customerCreate: Nuevo cliente
list: Listado
webPayments: Pagos Web
extendedList: Listado extendido
notifications: Notificaciones
defaulter: Morosos
createCustomer: Crear cliente
summary: Resumen
basicData: Datos básicos
fiscalData: Datos fiscales
billingData: Forma de pago
consignees: Consignatarios
notes: Notas
credits: Créditos
greuges: Greuges
balance: Balance
recoveries: Recobros
webAccess: Acceso web
log: Historial
sms: Sms
creditManagement: Gestión de crédito
creditContracts: Contratos de crédito
creditOpinion: Opinión de crédito
others: Otros
samples: Plantillas
consumption: Consumo
mandates: Mandatos
contacts: Contactos
webPayment: Pago web
fileManagement: Gestión documental
unpaid: Impago
list: list:
phone: Teléfono phone: Teléfono
email: Email email: Email
@ -310,17 +407,6 @@ customer:
hasCoreVnl: Recibido core VNL hasCoreVnl: Recibido core VNL
hasSepaVnl: Recibido B2B VNL hasSepaVnl: Recibido B2B VNL
entry: entry:
pageTitles:
entries: Entradas
list: Listado
summary: Resumen
basicData: Datos básicos
buys: Compras
notes: Notas
dms: Gestión documental
log: Historial
entryCreate: Nueva entrada
latestBuys: Últimas compras
list: list:
newEntry: Nueva entrada newEntry: Nueva entrada
landed: F. entrega landed: F. entrega
@ -525,18 +611,6 @@ ticket:
warehouse: Almacén warehouse: Almacén
agency: Agencia agency: Agencia
claim: claim:
pageTitles:
claims: Reclamaciones
list: Listado
claimCreate: Crear reclamación
summary: Resumen
basicData: Datos básicos
lines: Líneas
development: Trazabilidad
photos: Fotos
log: Historial
notes: Notas
action: Acción
list: list:
customer: Cliente customer: Cliente
assignedTo: Asignada a assignedTo: Asignada a
@ -600,14 +674,6 @@ claim:
noData: No hay imágenes/videos haz click aquí o arrastra y suelta el archivo noData: No hay imágenes/videos haz click aquí o arrastra y suelta el archivo
dragDrop: Arrástralo y sueltalo aquí dragDrop: Arrástralo y sueltalo aquí
invoiceOut: invoiceOut:
pageTitles:
invoiceOuts: Fact. emitidas
list: Listado
negativeBases: Bases Negativas
globalInvoicing: Facturación global
invoiceOutCreate: Crear fact. emitida
summary: Resumen
basicData: Datos básicos
list: list:
ref: Referencia ref: Referencia
issued: Fecha emisión issued: Fecha emisión
@ -675,15 +741,6 @@ invoiceOut:
errors: errors:
downloadCsvFailed: Error al descargar CSV downloadCsvFailed: Error al descargar CSV
order: order:
pageTitles:
order: Cesta
orderList: Listado
orderCreate: Nueva orden
summary: Resumen
basicData: Datos básicos
catalog: Catálogo
volume: Volumen
lines: Líneas
field: field:
salesPersonFk: Comercial salesPersonFk: Comercial
clientFk: Cliente clientFk: Cliente
@ -725,13 +782,6 @@ order:
price: Precio price: Precio
amount: Monto amount: Monto
shelving: shelving:
pageTitles:
shelving: Carros
shelvingList: Listado de carros
shelvingCreate: Nuevo carro
summary: Resumen
basicData: Datos básicos
log: Historial
list: list:
parking: Parking parking: Parking
priority: Prioridad priority: Prioridad
@ -757,17 +807,6 @@ parking:
info: Puedes buscar por código de parking info: Puedes buscar por código de parking
label: Buscar parking... label: Buscar parking...
invoiceIn: invoiceIn:
pageTitles:
invoiceIns: Fact. recibidas
list: Listado
invoiceInCreate: Crear fact. recibida
summary: Resumen
basicData: Datos básicos
vat: IVA
dueDay: Vencimiento
intrastat: Intrastat
corrective: Rectificativa
log: Historial
list: list:
ref: Referencia ref: Referencia
supplier: Proveedor supplier: Proveedor
@ -850,7 +889,6 @@ worker:
timeControl: Control de horario timeControl: Control de horario
locker: Taquilla locker: Taquilla
balance: Balance balance: Balance
formation: Formación
list: list:
name: Nombre name: Nombre
email: Email email: Email
@ -930,15 +968,6 @@ worker:
credit: Haber credit: Haber
concept: Concepto concept: Concepto
wagon: wagon:
pageTitles:
wagons: Vagones
wagonsList: Listado vagones
wagonCreate: Crear tipo
wagonEdit: Editar tipo
typesList: Listado tipos
typeCreate: Crear tipo
typeEdit: Editar tipo
wagonCounter: Contador de carros
type: type:
name: Nombre name: Nombre
submit: Guardar submit: Guardar
@ -967,36 +996,12 @@ wagon:
minHeightBetweenTrays: 'La distancia mínima entre bandejas es ' minHeightBetweenTrays: 'La distancia mínima entre bandejas es '
maxWagonHeight: 'La altura máxima del vagón es ' maxWagonHeight: 'La altura máxima del vagón es '
uncompleteTrays: Hay bandejas sin completar uncompleteTrays: Hay bandejas sin completar
route/roadmap:
pageTitles:
roadmap: Troncales
summary: Resumen
basicData: Datos básicos
stops: Paradas
roadmap:
pageTitles:
roadmap: Troncales
summary: Resumen
basicData: Datos básicos
stops: Paradas
route: route:
pageTitles:
routes: Rutas
cmrsList: Listado de CMRs
RouteList: Listado
routeCreate: Nueva ruta
basicData: Datos básicos
summary: Resumen
RouteRoadmap: Troncales
RouteRoadmapCreate: Crear troncal
tickets: Tickets
log: Historial
autonomous: Autónomos
cmr: cmr:
list: list:
results: resultados results: resultados
cmrFk: Id CMR cmrFk: Id CMR
hasCmrDms: Adjuntado en gestdoc hasCmrDms: Gestdoc
'true': 'true':
'false': 'No' 'false': 'No'
ticketFk: Id ticket ticketFk: Id ticket
@ -1019,22 +1024,6 @@ route:
volume: Volumen volume: Volumen
finished: Finalizada finished: Finalizada
supplier: supplier:
pageTitles:
suppliers: Proveedores
supplier: Proveedor
list: Listado
supplierCreate: Nuevo proveedor
summary: Resumen
basicData: Datos básicos
fiscalData: Datos fiscales
billingData: Forma de pago
log: Historial
accounts: Cuentas
contacts: Contactos
addresses: Direcciones
consumption: Consumo
agencyTerm: Acuerdo agencia
dms: Gestión documental
list: list:
payMethod: Método de pago payMethod: Método de pago
payDeadline: Plazo de pago payDeadline: Plazo de pago
@ -1126,16 +1115,6 @@ supplier:
date: Fecha date: Fecha
reference: Referencia reference: Referencia
travel: travel:
pageTitles:
travel: Envíos
list: Listado
create: Crear
summary: Resumen
extraCommunity: Extra comunitarios
travelCreate: Nuevo envío
basicData: Datos básicos
history: Historial
thermographs: Termógrafos
summary: summary:
confirmed: Confirmado confirmed: Confirmado
entryId: Id entrada entryId: Id entrada
@ -1182,24 +1161,6 @@ travel:
travelFileDescription: 'Id envío { travelId }' travelFileDescription: 'Id envío { travelId }'
file: Fichero file: Fichero
item: item:
pageTitles:
items: Artículos
list: Listado
diary: Histórico
tags: Etiquetas
fixedPrice: Precios fijados
buyRequest: Peticiones de compra
wasteBreakdown: Deglose de mermas
itemCreate: Nuevo artículo
basicData: 'Datos básicos'
tax: 'IVA'
botanical: 'Botánico'
barcode: 'Código de barras'
log: Historial
shelving: Carros
itemTypeCreate: Nueva familia
family: Familia
lastEntries: Últimas entradas
descriptor: descriptor:
item: Artículo item: Artículo
buyer: Comprador buyer: Comprador
@ -1285,27 +1246,6 @@ item:
achieved: 'Conseguido' achieved: 'Conseguido'
concept: 'Concepto' concept: 'Concepto'
state: 'Estado' state: 'Estado'
item/itemType:
pageTitles:
itemType: Familia
basicData: Datos básicos
summary: Resumen
zone:
pageTitles:
zones: Zonas
list: Zonas
deliveryList: Días de entrega
upcomingList: Próximos repartos
role:
pageTitles:
zones: Zonas
list: Zonas
deliveryList: Días de entrega
upcomingList: Próximos repartos
monitor:
pageTitles:
monitors: Monitores
list: Listado
components: components:
topbar: {} topbar: {}
itemsFilterPanel: itemsFilterPanel:

View File

@ -224,7 +224,7 @@ async function changeState(value) {
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle <VnTitle
:url="`#/claim/${entityId}/basic-data`" :url="`#/claim/${entityId}/basic-data`"
:text="t('claim.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
<VnLv <VnLv
:label="t('claim.summary.created')" :label="t('claim.summary.created')"

View File

@ -69,7 +69,7 @@ const filterOptions = {
<template #form="{ data, validate, filter }"> <template #form="{ data, validate, filter }">
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">
<VnInput <VnInput
:label="t('Comercial name')" :label="t('globals.name')"
:rules="validate('client.socialName')" :rules="validate('client.socialName')"
autofocus autofocus
clearable clearable

View File

@ -73,7 +73,7 @@ const creditWarning = computed(() => {
:text="t('customer.summary.basicData')" :text="t('customer.summary.basicData')"
/> />
<VnLv :label="t('customer.summary.customerId')" :value="entity.id" /> <VnLv :label="t('customer.summary.customerId')" :value="entity.id" />
<VnLv :label="t('customer.summary.name')" :value="entity.name" /> <VnLv :label="t('globals.name')" :value="entity.name" />
<VnLv :label="t('customer.summary.contact')" :value="entity.contact" /> <VnLv :label="t('customer.summary.contact')" :value="entity.contact" />
<VnLv :value="entity.phone"> <VnLv :value="entity.phone">
<template #label> <template #label>

View File

@ -38,7 +38,7 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
label: t('customer.extendedList.tableVisibleColumns.name'), label: t('globals.name'),
name: 'name', name: 'name',
isTitle: true, isTitle: true,
create: true, create: true,
@ -65,6 +65,8 @@ const columns = computed(() => [
url: 'Workers/activeWithInheritedRole', url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'], fields: ['id', 'name'],
where: { role: 'salesPerson' }, where: { role: 'salesPerson' },
optionFilter: 'firstName',
useLike: false,
}, },
create: true, create: true,
columnField: { columnField: {
@ -406,6 +408,7 @@ function handleLocation(data, location) {
default-mode="table" default-mode="table"
redirect="customer" redirect="customer"
auto-load auto-load
:disable-option="{ card: true }"
> >
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnLocation <VnLocation

View File

@ -0,0 +1,120 @@
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { QBtn } from 'quasar';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import { usePrintService } from 'composables/usePrintService';
const { openReport } = usePrintService();
const route = useRoute();
const { t } = useI18n();
const $props = defineProps({
id: {
type: String,
required: false,
default: null,
},
});
const entityId = computed(() => $props.id || route.params.id);
const entriesTableColumns = computed(() => [
{
align: 'left',
name: 'itemFk',
field: 'itemFk',
label: t('globals.id'),
},
{
align: 'left',
name: 'item',
label: t('entry.summary.item'),
field: (row) => row.item.name,
},
{
align: 'left',
name: 'packagingFk',
label: t('entry.summary.package'),
field: 'packagingFk',
},
{
align: 'left',
name: 'stickers',
label: t('entry.summary.stickers'),
field: 'stickers',
},
{
align: 'left',
name: 'packing',
label: t('entry.summary.packing'),
field: 'packing',
},
{
align: 'left',
name: 'grouping',
label: t('entry.summary.grouping'),
field: 'grouping',
},
]);
</script>
<template>
<QDialog ref="dialogRef">
<QCard style="min-width: 800px">
<QCardSection class="row items-center q-pb-none">
<QAvatar
:icon="icon"
color="primary"
text-color="white"
size="xl"
v-if="icon"
/>
<span class="text-h6 text-grey">{{ title }}</span>
<QSpace />
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
</QCardSection>
<QCardActions align="right">
<QBtn
:label="t('Print buys')"
color="primary"
icon="print"
:loading="isLoading"
@click="openReport(`Entries/${entityId}/buy-label`)"
unelevated
autofocus
/>
</QCardActions>
<QCardSection class="row items-center">
<VnPaginate
ref="entryBuysPaginateRef"
:limit="0"
data-key="EntryBuys"
:url="`Entries/${entityId}/getBuys`"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="entriesTableColumns"
row-key="id"
flat
dense
class="q-ml-lg"
:grid="$q.screen.lt.md"
:no-data-label="t('globals.noResults')"
>
<template #body="props">
<QTr>
<QTd v-for="col in props.cols" :key="col.name">
{{ col.value }}
</QTd>
</QTr>
</template>
</QTable>
</template>
</VnPaginate>
</QCardSection>
</QCard>
</QDialog>
</template>

View File

@ -0,0 +1,106 @@
<script setup>
import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
import { toDate } from 'src/filters/index';
import { useQuasar } from 'quasar';
import EntryBuysTableDialog from './EntryBuysTableDialog.vue';
import VnTable from 'components/VnTable/VnTable.vue';
const stateStore = useStateStore();
const { t } = useI18n();
const quasar = useQuasar();
onMounted(async () => {
stateStore.rightDrawer = true;
});
const columns = computed(() => [
{
align: 'left',
name: 'id',
label: t('customer.extendedList.tableVisibleColumns.id'),
chip: {
condition: () => true,
},
isId: true,
isTitle: false,
},
{
align: 'left',
label: t('shipped'),
name: 'shipped',
isTitle: false,
create: true,
cardVisible: true,
format: ({ shipped }) => toDate(shipped),
},
{
align: 'left',
label: t('landed'),
name: 'landed',
isTitle: false,
create: true,
cardVisible: false,
format: ({ landed }) => toDate(landed),
},
{
align: 'left',
label: t('globals.wareHouseIn'),
name: 'warehouseInName',
isTitle: false,
cardVisible: true,
create: false,
},
{
align: 'right',
name: 'tableActions',
computed,
actions: [
{
title: t('printBuys'),
icon: 'print',
action: (row) => printBuys(row.id),
},
],
},
]);
const printBuys = (rowId) => {
quasar.dialog({
component: EntryBuysTableDialog,
componentProps: {
id: rowId,
},
});
};
</script>
<template>
<VnSearchbar
data-key="EntryList"
url="Entries/filter"
:label="t('Search entries')"
:info="t('You can search by entry reference')"
/>
<QPage class="column items-center q-pa-md">
<div class="vn-card-list">
<VnTable
ref="myEntriesRef"
data-key="myEntriesList"
url="Entries/filter"
:order="['landed DESC', 'id DESC']"
:columns="columns"
default-mode="card"
auto-load
>
</VnTable>
</div>
</QPage>
</template>
<i18n>
es:
Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada
</i18n>

View File

@ -8,3 +8,4 @@ entryFilter:
reference: Reference reference: Reference
landed: Landed landed: Landed
shipped: Shipped shipped: Shipped
printBuys: Print buys

View File

@ -1,5 +1,6 @@
Search entries: Buscar entradas Search entries: Buscar entradas
You can search by entry reference: Puedes buscar por referencia de la entrada You can search by entry reference: Puedes buscar por referencia de la entrada
entryList: entryList:
list: list:
inventoryEntry: Es inventario inventoryEntry: Es inventario
@ -11,3 +12,4 @@ entryFilter:
landed: F. llegada landed: F. llegada
shipped: F. salida shipped: F. salida
Print buys: Imprimir etiquetas

View File

@ -209,7 +209,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
@ -240,7 +240,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
@ -265,7 +265,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
@ -289,7 +289,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('invoiceIn.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">

View File

@ -200,16 +200,10 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<VnInputDate <VnInputDate
:label="t('lastEntries.since')" :label="t('lastEntries.since')"
dense dense
emit-date-format
v-model="from" v-model="from"
class="q-mr-lg" class="q-mr-lg"
/> />
<VnInputDate <VnInputDate :label="t('lastEntries.to')" dense v-model="to" />
:label="t('lastEntries.to')"
dense
emit-date-format
v-model="to"
/>
</div> </div>
<QSpace /> <QSpace />
<div id="st-actions"></div> <div id="st-actions"></div>

View File

@ -192,7 +192,6 @@ const decrement = (paramsObj, key) => {
v-model="params.from" v-model="params.from"
@update:model-value="searchFn()" @update:model-value="searchFn()"
is-outlined is-outlined
emit-date-format
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -203,7 +202,6 @@ const decrement = (paramsObj, key) => {
v-model="params.to" v-model="params.to"
@update:model-value="searchFn()" @update:model-value="searchFn()"
is-outlined is-outlined
emit-date-format
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>

View File

@ -169,7 +169,6 @@ const columns = computed(() => [
<VnInputDate <VnInputDate
:label="t('salesClientsTable.from')" :label="t('salesClientsTable.from')"
dense dense
emit-date-format
v-model="from" v-model="from"
class="q-mr-lg" class="q-mr-lg"
style="width: 150px" style="width: 150px"
@ -177,7 +176,6 @@ const columns = computed(() => [
<VnInputDate <VnInputDate
:label="t('salesClientsTable.to')" :label="t('salesClientsTable.to')"
dense dense
emit-date-format
v-model="to" v-model="to"
style="width: 150px" style="width: 150px"
/> />

View File

@ -2,7 +2,6 @@
import VnCard from 'components/common/VnCard.vue'; import VnCard from 'components/common/VnCard.vue';
import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue'; import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue';
import OrderFilter from './OrderFilter.vue'; import OrderFilter from './OrderFilter.vue';
import OrderSearchbar from './OrderSearchbar.vue';
</script> </script>
<template> <template>
<VnCard <VnCard
@ -11,9 +10,7 @@ import OrderSearchbar from './OrderSearchbar.vue';
:descriptor="OrderDescriptor" :descriptor="OrderDescriptor"
:filter-panel="OrderFilter" :filter-panel="OrderFilter"
search-data-key="OrderList" search-data-key="OrderList"
> searchbar-label="Search order"
<template #searchbar> searchbar-info="ypu can search by order id or name"
<OrderSearchbar /> />
</template>
</VnCard>
</template> </template>

View File

@ -37,6 +37,10 @@ const selectedOrder = ref(null);
const selectedOrderField = ref(null); const selectedOrderField = ref(null);
const moreFields = ref([]); const moreFields = ref([]);
const moreFieldsOrder = ref([]); const moreFieldsOrder = ref([]);
const selectedTag = ref(null);
const tagValues = ref([{}]);
const tagOptions = ref([]);
const createValue = (val, done) => { const createValue = (val, done) => {
if (val.length > 2) { if (val.length > 2) {
if (!tagOptions.value.includes(val)) { if (!tagOptions.value.includes(val)) {
@ -95,10 +99,6 @@ function exprBuilder(param, value) {
} }
} }
const selectedTag = ref(null);
const tagValues = ref([{}]);
const tagOptions = ref([]);
const applyTagFilter = (params, search) => { const applyTagFilter = (params, search) => {
if (!tagValues.value?.length) { if (!tagValues.value?.length) {
params.tagGroups = null; params.tagGroups = null;
@ -139,34 +139,22 @@ const onOrderChange = (value, params) => {
}; };
const onOrderFieldChange = (value, params) => { const onOrderFieldChange = (value, params) => {
const tagObj = JSON.parse(params.orderBy); // esto donde va const tagObj = JSON.parse(params.orderBy);
const fields = {
Relevancy: (value) => value + ' DESC, name',
ColorAndPrice: 'showOrder, price',
Name: 'name',
Price: 'price',
};
let tagField = fields[value];
if (!tagField) return;
if (typeof tagField === 'function') tagField = tagField(value);
tagObj.field = tagField;
params.orderBy = JSON.stringify(tagObj);
switch (value) { switch (value) {
case 'Relevancy': case 'Relevancy':
tagObj.field = value + ' DESC, name'; tagObj.name = value + ' DESC, name';
params.orderBy = JSON.stringify(tagObj); params.orderBy = JSON.stringify(tagObj);
break; break;
case 'ColorAndPrice': case 'ColorAndPrice':
tagObj.field = 'showOrder, price'; tagObj.name = 'showOrder, price';
params.orderBy = JSON.stringify(tagObj); params.orderBy = JSON.stringify(tagObj);
break; break;
case 'Name': case 'Name':
tagObj.field = 'name'; tagObj.name = 'name';
params.orderBy = JSON.stringify(tagObj); params.orderBy = JSON.stringify(tagObj);
break; break;
case 'Price': case 'Price':
tagObj.field = 'price'; tagObj.name = 'price';
params.orderBy = JSON.stringify(tagObj); params.orderBy = JSON.stringify(tagObj);
break; break;
} }
@ -308,6 +296,7 @@ const useLang = (values) => {
v-model="selectedOrder" v-model="selectedOrder"
:options="moreFields" :options="moreFields"
option-label="label" option-label="label"
option-value="way"
dense dense
outlined outlined
rounded rounded

View File

@ -27,7 +27,7 @@ const dialog = ref(null);
<div class="container order-catalog-item overflow-hidden"> <div class="container order-catalog-item overflow-hidden">
<QCard class="card shadow-6"> <QCard class="card shadow-6">
<div class="img-wrapper"> <div class="img-wrapper">
<VnImg :id="item.id" class="image" /> <VnImg :id="item.id" zoom-size="lg" class="image" />
<div v-if="item.hex" class="item-color-container"> <div v-if="item.hex" class="item-color-container">
<div <div
class="item-color" class="item-color"

View File

@ -7,6 +7,8 @@ import VnLv from 'components/ui/VnLv.vue';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue'; import FetchedTags from 'components/ui/FetchedTags.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
@ -62,6 +64,10 @@ const detailsColumns = ref([
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle
:url="`#/order/${entity.id}/basic-data`"
:text="t('globals.pageTitles.basicData')"
/>
<VnLv label="ID" :value="entity.id" /> <VnLv label="ID" :value="entity.id" />
<VnLv :label="t('order.summary.nickname')" dash> <VnLv :label="t('order.summary.nickname')" dash>
<template #value> <template #value>
@ -81,6 +87,10 @@ const detailsColumns = ref([
/> />
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle
:url="`#/order/${entity.id}/basic-data`"
:text="t('globals.pageTitles.basicData')"
/>
<VnLv <VnLv
:label="t('order.summary.created')" :label="t('order.summary.created')"
:value="toDateHourMinSec(entity?.created)" :value="toDateHourMinSec(entity?.created)"
@ -116,14 +126,13 @@ const detailsColumns = ref([
/> />
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<p class="header"> <VnTitle :text="t('globals.pageTitles.notes')" />
{{ t('order.summary.notes') }}
</p>
<p v-if="entity?.note" class="no-margin"> <p v-if="entity?.note" class="no-margin">
{{ entity?.note }} {{ entity?.note }}
</p> </p>
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle :text="t('order.summary.total')" />
<VnLv> <VnLv>
<template #label> <template #label>
<span class="text-h6">{{ t('order.summary.subtotal') }}</span> <span class="text-h6">{{ t('order.summary.subtotal') }}</span>
@ -152,9 +161,7 @@ const detailsColumns = ref([
</VnLv> </VnLv>
</QCard> </QCard>
<QCard> <QCard>
<p class="header"> <VnTitle :text="t('order.summary.details')" />
{{ t('order.summary.details') }}
</p>
<QTable :columns="detailsColumns" :rows="entity?.rows" flat> <QTable :columns="detailsColumns" :rows="entity?.rows" flat>
<template #header="props"> <template #header="props">
<QTr :props="props"> <QTr :props="props">
@ -168,7 +175,10 @@ const detailsColumns = ref([
<template #body="props"> <template #body="props">
<QTr :props="props"> <QTr :props="props">
<QTd key="item" :props="props" class="item"> <QTd key="item" :props="props" class="item">
{{ props.row.item?.id }} <span class="link">
{{ props.row.item?.id }}
<ItemDescriptorProxy :id="props.row.item?.id" />
</span>
</QTd> </QTd>
<QTd key="description" :props="props" class="description"> <QTd key="description" :props="props" class="description">
<div class="name"> <div class="name">

View File

@ -70,6 +70,7 @@ function extractValueTags(items) {
:user-params="catalogParams" :user-params="catalogParams"
auto-load auto-load
@on-fetch="extractTags" @on-fetch="extractTags"
:update-router="false"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<div class="catalog-list"> <div class="catalog-list">

View File

@ -9,7 +9,7 @@ import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
import VnLv from 'components/ui/VnLv.vue'; import VnLv from 'components/ui/VnLv.vue';
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import OrderFilter from 'pages/Order/Card/OrderFilter.vue'; import OrderFilter from 'pages/Order/Card/OrderFilter.vue';
import OrderSummary from 'pages/Order/Card/OrderSummary.vue'; import OrderSummary from 'pages/Order/Card/OrderSummary.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -28,7 +28,11 @@ function navigate(id) {
} }
</script> </script>
<template> <template>
<OrderSearchbar /> <VnSearchbar
data-key="OrderList"
:label="t('Search order')"
:info="t('You can search orders by reference')"
/>
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
<OrderFilter data-key="OrderList" /> <OrderFilter data-key="OrderList" />

View File

@ -1,65 +1,84 @@
<script setup> <script setup>
import VnPaginate from 'src/components/ui/VnPaginate.vue'; import { computed } from 'vue';
import CardList from 'src/components/ui/CardList.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnTable from 'components/VnTable/VnTable.vue';
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
function navigate(id) { function navigate(id) {
router.push({ path: `/agency/${id}` }); router.push({ path: `/agency/${id}` });
} }
function exprBuilder(param, value) { const exprBuilder = (param, value) => {
if (!value) return; if (!value) return;
if (param !== 'search') return; if (param !== 'search') return;
if (!isNaN(value)) return { id: value }; if (!isNaN(value)) return { id: value };
return { name: { like: `%${value}%` } }; return { name: { like: `%${value}%` } };
} };
const columns = computed(() => [
{
align: 'left',
name: 'id',
label: 'Id',
chip: {
condition: () => true,
},
isId: true,
},
{
align: 'left',
label: t('globals.name'),
name: 'name',
isTitle: true,
},
{
align: 'left',
label: t('isOwn'),
name: 'isOwn',
component: 'checkbox',
cardVisible: true,
},
{
align: 'left',
label: t('isAnyVolumeAllowed'),
name: 'isAnyVolumeAllowed',
component: 'checkbox',
cardVisible: true,
disable: true,
},
{
align: 'right',
label: '',
name: 'tableActions',
actions: [
{
title: t('Client ticket list'),
icon: 'preview',
action: (row) => navigate(row.id),
},
],
},
]);
</script> </script>
<template> <template>
<VnSearchbar <VnSearchbar
:info="t('You can search by name')" :info="t('You can search by name')"
:label="t('Search agency')" :label="t('Search agency')"
data-key="AgencyList" data-key="AgencyList"
url="Agencies" :expr-builder="exprBuilder"
/>
<VnTable
ref="tableRef"
data-key="AgencyList"
url="Agencies"
order="name"
:columns="columns"
:right-search="false"
:use-model="true"
/> />
<QPage class="column items-center q-pa-md">
<div class="vn-card-list">
<VnPaginate
data-key="AgencyList"
url="Agencies"
order="name"
:expr-builder="exprBuilder"
auto-load
>
<template #body="{ rows }">
<CardList
:id="row.id"
:key="row.id"
:title="row.name"
@click="navigate(row.id)"
v-for="row of rows"
>
<template #list-items>
<QCheckbox
:label="t('isOwn')"
v-model="row.isOwn"
:disable="true"
/>
<QCheckbox
:label="t('isAnyVolumeAllowed')"
v-model="row.isAnyVolumeAllowed"
:disable="true"
/>
</template>
</CardList>
</template>
</VnPaginate>
</div>
</QPage>
</template> </template>
<i18n> <i18n>
es: es:

View File

@ -18,12 +18,10 @@ const entityId = computed(() => $props.id || useRoute().params.id);
<template #header="{ entity: agency }">{{ agency.name }}</template> <template #header="{ entity: agency }">{{ agency.name }}</template>
<template #body="{ entity: agency }"> <template #body="{ entity: agency }">
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <VnTitle
<VnTitle :url="`#/agency/${entityId}/basic-data`"
:url="`#/agency/${entityId}/basic-data`" :text="t('globals.pageTitles.basicData')"
:text="t('globals.pageTitles.basicData')" />
/>
</QCardSection>
<VnLv :label="t('globals.name')" :value="agency.name" /> <VnLv :label="t('globals.name')" :value="agency.name" />
<QCheckbox <QCheckbox
:label="t('agency.isOwn')" :label="t('agency.isOwn')"

View File

@ -9,7 +9,6 @@ const { t } = useI18n();
data-key="RouteList" data-key="RouteList"
:label="t('Search route')" :label="t('Search route')"
:info="t('You can search by route reference')" :info="t('You can search by route reference')"
custom-route-redirect-name="RouteList"
/> />
</template> </template>

View File

@ -154,7 +154,7 @@ const warehouses = ref();
es: es:
params: params:
cmrFk: Id cmr cmrFk: Id cmr
hasCmrDms: Adjuntado en gestdoc hasCmrDms: Gestdoc
ticketFk: Id ticket ticketFk: Id ticket
country: País country: País
clientFk: Id cliente clientFk: Id cliente

View File

@ -12,6 +12,8 @@ import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import { useStateStore } from 'src/stores/useStateStore'; import { useStateStore } from 'src/stores/useStateStore';
import VnTable from 'components/VnTable/VnTable.vue';
const { t } = useI18n(); const { t } = useI18n();
const { getTokenMultimedia } = useSession(); const { getTokenMultimedia } = useSession();
const token = getTokenMultimedia(); const token = getTokenMultimedia();
@ -107,7 +109,16 @@ function downloadPdfs() {
} }
</script> </script>
<template> <template>
<RightMenu> <VnTable
ref="tableRef"
data-key="CmrList"
url="Routes/cmrs"
order="cmrFk DESC"
:columns="columns"
:right-search="true"
:use-model="true"
/>
<!-- <RightMenu>
<template #right-panel> <template #right-panel>
<CmrFilter data-key="CmrList" /> <CmrFilter data-key="CmrList" />
</template> </template>
@ -187,8 +198,8 @@ function downloadPdfs() {
<QTooltip> <QTooltip>
{{ t('route.cmr.list.downloadCmrs') }} {{ t('route.cmr.list.downloadCmrs') }}
</QTooltip> </QTooltip>
</QPageSticky> </QPageSticky>
</div> </div> -->
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.list { .list {

View File

@ -1,29 +1,32 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import { useArrayData } from 'composables/useArrayData';
import { computed, onMounted, ref } from 'vue';
import { dashIfEmpty, toHour } from 'src/filters'; import { dashIfEmpty, toHour } from 'src/filters';
import { computed, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useValidator } from 'composables/useValidator'; import { useValidator } from 'composables/useValidator';
import { useSession } from 'composables/useSession'; import { useSession } from 'composables/useSession';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useArrayData } from 'composables/useArrayData';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
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 FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.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 RightMenu from 'src/components/common/RightMenu.vue';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue'; import VnInputTime from 'components/common/VnInputTime.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.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 { t } = useI18n();
const { validate } = useValidator(); const { validate } = useValidator();
@ -40,90 +43,100 @@ const allColumnNames = ref([]);
const confirmationDialog = ref(false); const confirmationDialog = ref(false);
const startingDate = ref(null); const startingDate = ref(null);
const refreshKey = ref(0); const refreshKey = ref(0);
const router = useRouter();
const stateStore = useStateStore();
const columns = computed(() => [ const columns = computed(() => [
{ {
name: 'Id', align: 'left',
label: t('Id'), name: 'id',
field: (row) => row.id, label: 'Id',
sortable: true, chip: {
align: 'center', condition: () => true,
},
isId: true,
columnFilter: {
name: 'search',
},
}, },
{ {
align: 'left',
name: 'worker', name: 'worker',
label: t('Worker'), label: t('Worker'),
field: (row) => row.workerUserName, create: true,
sortable: true, component: 'select',
align: 'left', attrs: {
url: 'payrollComponents',
fields: ['id', 'name'],
},
cardVisible: true,
}, },
{ {
name: 'agency', name: 'agency',
label: t('Agency'), label: t('Agency'),
field: (row) => row.agencyName,
sortable: true,
align: 'left', align: 'left',
isTitle: true,
cardVisible: true,
create: true,
}, },
{ {
name: 'vehicle', name: 'vehicle',
label: t('Vehicle'), label: t('Vehicle'),
field: (row) => row.vehiclePlateNumber,
sortable: true,
align: 'left', align: 'left',
cardVisible: true,
create: true,
}, },
{ {
name: 'date', name: 'date',
label: t('Date'), label: t('Date'),
field: (row) => row.created,
sortable: true,
align: 'left', align: 'left',
cardVisible: true,
create: true,
}, },
{ {
name: 'volume', name: 'volume',
label: 'm³', label: 'm³',
field: (row) => dashIfEmpty(row.m3),
sortable: true,
align: 'center', align: 'center',
cardVisible: true,
}, },
{ {
name: 'description', name: 'description',
label: t('Description'), label: t('Description'),
field: (row) => row.description,
sortable: true,
align: 'left', align: 'left',
isTitle: true,
create: true,
}, },
{ {
name: 'started', name: 'started',
label: t('hourStarted'), label: t('hourStarted'),
field: (row) => toHour(row.started),
sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'finished', name: 'finished',
label: t('hourFinished'), label: t('hourFinished'),
field: (row) => toHour(row.finished),
sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'isServed', name: 'isServed',
label: t('Served'), label: t('Served'),
field: (row) => Boolean(row.isOk),
sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'actions',
label: '',
sortable: false,
align: 'right', align: 'right',
label: 'asdasd',
name: 'tableActions',
actions: [
{
title: t('Client ticket list'),
icon: 'preview',
action: (row) => navigate(row.id),
},
],
}, },
]); ]);
const arrayData = useArrayData('EntryLatestBuys', { function navigate(id) {
url: 'Buys/latestBuysFilter', router.push({ path: `/route/${id}` });
order: ['itemFk DESC'], }
});
const updateRoute = async (route) => { const updateRoute = async (route) => {
try { try {
@ -182,19 +195,19 @@ const openTicketsDialog = (id) => {
.onOk(() => refreshKey.value++); .onOk(() => refreshKey.value++);
}; };
onMounted(async () => { const isMounted = ref(false);
allColumnNames.value = columns.value.map((col) => col.name); onMounted(() => {
await arrayData.fetch({ append: false }); console.log('Lo impoosible si es posible ');
}); });
</script> </script>
<template> <template>
<RouteSearchbar /> <RouteSearchbar />
<RightMenu> <!-- <RightMenu>
<template #right-panel> <template #right-panel>
<RouteFilter data-key="RouteList" /> <RouteFilter data-key="RouteList" />
</template> </template>
</RightMenu> </RightMenu> -->
<QDialog v-model="confirmationDialog"> <QDialog v-model="confirmationDialog">
<QCard style="min-width: 350px"> <QCard style="min-width: 350px">
<QCardSection> <QCardSection>
@ -227,48 +240,53 @@ onMounted(async () => {
/> />
<FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load /> <FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
<FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load /> <FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
<QPage class="column items-center"> <VnSubToolbar>
<VnSubToolbar> <QBtn
<template #st-data> icon="vn:clone"
<TableVisibleColumns color="primary"
class="LeftIcon" class="q-mr-sm"
:all-columns="allColumnNames" :disable="!selectedRows?.length"
table-code="routesList" @click="confirmationDialog = true"
labels-traductions-path="route.columnLabels" >
@on-config-saved="visibleColumns = [...$event]" <QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
/> </QBtn>
</template> <QBtn
<template #st-actions> icon="cloud_download"
<QBtn color="primary"
icon="vn:clone" class="q-mr-sm"
color="primary" :disable="!selectedRows?.length"
class="q-mr-sm" @click="showRouteReport"
:disable="!selectedRows?.length" >
@click="confirmationDialog = true" <QTooltip>{{ t('Download selected routes as PDF') }}</QTooltip>
> </QBtn>
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip> <QBtn
</QBtn> icon="check"
<QBtn color="primary"
icon="cloud_download" class="q-mr-sm"
color="primary" :disable="!selectedRows?.length"
class="q-mr-sm" @click="markAsServed()"
:disable="!selectedRows?.length" >
@click="showRouteReport" <QTooltip>{{ t('Mark as served') }}</QTooltip>
> </QBtn>
<QTooltip>{{ t('Download selected routes as PDF') }}</QTooltip> </VnSubToolbar>
</QBtn> <VnTable
<QBtn ref="tableRef"
icon="check" data-key="RouteList"
color="primary" url="Routes/filter"
class="q-mr-sm" :columns="columns"
:disable="!selectedRows?.length" :right-search="true"
@click="markAsServed()" default-mode="table"
> :is-editable="true"
<QTooltip>{{ t('Mark as served') }}</QTooltip> :create="{
</QBtn> urlCreate: 'Routes',
</template> title: t('Create route'),
</VnSubToolbar> onDataSaved: () => tableRef.reload(),
<div class="route-list"> }"
save-url="WorkerIncomes/crud"
:disable-option="{ card: true }"
/>
<!-- >
<VnPaginate <VnPaginate
:key="refreshKey" :key="refreshKey"
data-key="RouteList" data-key="RouteList"
@ -464,19 +482,18 @@ onMounted(async () => {
</QTd> </QTd>
</template> </template>
</QTable> </QTable>
</div> </div>
</template> </template>
</VnPaginate> </VnPaginate>
</div> </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" />
<QTooltip> <QTooltip>
{{ t('newRoute') }} {{ t('newRoute') }}
</QTooltip> </QTooltip>
</RouterLink> </RouterLink>
</QPageSticky> </QPageSticky> -->
</QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -2,7 +2,7 @@
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import LeftMenu from 'src/components/LeftMenu.vue'; import LeftMenu from 'src/components/LeftMenu.vue';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
onMounted(() => (stateStore.leftDrawer = false)); onMounted(() => (stateStore.leftDrawer = false));
</script> </script>
@ -14,6 +14,7 @@ onMounted(() => (stateStore.leftDrawer = false));
</QScrollArea> </QScrollArea>
</QDrawer> </QDrawer>
<QPageContainer> <QPageContainer>
<RouterView></RouterView> <VnSubToolbar />
<RouterView v-if="stateStore.isSubToolbarShown()"></RouterView>
</QPageContainer> </QPageContainer>
</template> </template>

View File

@ -51,7 +51,7 @@ const filter = {
class="header header-link" class="header header-link"
:to="{ name: 'ShelvingBasicData', params: { id: entityId } }" :to="{ name: 'ShelvingBasicData', params: { id: entityId } }"
> >
{{ t('shelving.pageTitles.basicData') }} {{ t('globals.pageTitles.basicData') }}
<QIcon name="open_in_new" /> <QIcon name="open_in_new" />
</RouterLink> </RouterLink>
<VnLv :label="t('shelving.summary.code')" :value="entity.code" /> <VnLv :label="t('shelving.summary.code')" :value="entity.code" />

View File

@ -17,15 +17,6 @@ const props = defineProps({
}, },
}); });
const from = Date.vnNew();
const to = Date.vnNew();
to.setDate(to.getDate() + 1);
const defaultParams = {
from: toDateString(from),
to: toDateString(to),
};
const workers = ref(); const workers = ref();
const provinces = ref(); const provinces = ref();
const states = ref(); const states = ref();
@ -44,11 +35,7 @@ const warehouses = ref();
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
auto-load auto-load
/> />
<VnFilterPanel <VnFilterPanel :data-key="props.dataKey" :search-button="true">
:data-key="props.dataKey"
:params="defaultParams"
:search-button="true"
>
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong> <strong>{{ t(`params.${tag.label}`) }}: </strong>

View File

@ -26,8 +26,8 @@ const to = Date.vnNew();
to.setDate(to.getDate() + 1); to.setDate(to.getDate() + 1);
const userParams = { const userParams = {
from: toDateString(from), from: from.toISOString(),
to: toDateString(to), to: to.toISOString(),
}; };
function navigate(id) { function navigate(id) {

View File

@ -49,7 +49,7 @@ const entriesTableColumns = computed(() => {
showValue: false, showValue: false,
}, },
{ {
label: t('supplier.pageTitles.supplier'), label: t('globals.pageTitles.supplier'),
field: 'supplierName', field: 'supplierName',
name: 'supplierName', name: 'supplierName',
align: 'left', align: 'left',
@ -248,7 +248,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('travel.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" /> <VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" />
@ -266,7 +266,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('travel.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" /> <VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" />
@ -284,7 +284,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnTitle <VnTitle
:url="getLink('basic-data')" :url="getLink('basic-data')"
:text="t('travel.pageTitles.basicData')" :text="t('globals.pageTitles.basicData')"
/> />
</QCardSection> </QCardSection>
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" /> <VnLv :label="t('globals.agency')" :value="travel.agency?.name" />

View File

@ -140,7 +140,7 @@ const columns = computed(() => [
sortable: true, sortable: true,
}, },
{ {
label: t('supplier.pageTitles.supplier'), label: t('globals.pageTitles.supplier'),
field: 'cargoSupplierNickname', field: 'cargoSupplierNickname',
name: 'cargoSupplierNickname', name: 'cargoSupplierNickname',
align: 'left', align: 'left',

View File

@ -218,7 +218,7 @@ warehouses();
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelect
:label="t('supplier.pageTitles.supplier')" :label="t('globals.pageTitles.supplier')"
v-model="params.cargoSupplierFk" v-model="params.cargoSupplierFk"
:options="suppliersOptions" :options="suppliersOptions"
option-value="id" option-value="id"

View File

@ -74,7 +74,7 @@ const agencyOptions = ref([]);
type="number" type="number"
min="0" min="0"
/> />
<VnInputTime v-model="data.hour" :label="t('Closing')" clearable /> <VnInputTime v-model="data.hour" :label="t('Closing')" />
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">

View File

@ -11,7 +11,6 @@ import { useState } from 'src/composables/useState';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { useRole } from 'src/composables/useRole'; import { useRole } from 'src/composables/useRole';
import { useUserConfig } from 'src/composables/useUserConfig'; import { useUserConfig } from 'src/composables/useUserConfig';
import { toLowerCamel } from 'src/filters';
import { useTokenConfig } from 'src/composables/useTokenConfig'; import { useTokenConfig } from 'src/composables/useTokenConfig';
import { useAcl } from 'src/composables/useAcl'; import { useAcl } from 'src/composables/useAcl';
@ -79,13 +78,11 @@ export default route(function (/* { store, ssrContext } */) {
let title = t(`login.title`); let title = t(`login.title`);
const matches = to.matched; const matches = to.matched;
let moduleName;
if (matches && matches.length > 1) { if (matches && matches.length > 1) {
const module = matches[1]; const module = matches[1];
const moduleTitle = module.meta && module.meta.title; const moduleTitle = module.meta && module.meta.title;
moduleName = toLowerCamel(module.name);
if (moduleTitle) { if (moduleTitle) {
title = t(`${moduleName}.pageTitles.${moduleTitle}`); title = t(`globals.pageTitles.${moduleTitle}`);
} }
} }
@ -94,7 +91,7 @@ export default route(function (/* { store, ssrContext } */) {
if (childPageTitle && matches.length > 2) { if (childPageTitle && matches.length > 2) {
if (title != '') title += ': '; if (title != '') title += ': ';
const moduleLocale = `${moduleName}.pageTitles.${childPageTitle}`; const moduleLocale = `globals.pageTitles.${childPageTitle}`;
const pageTitle = te(moduleLocale) const pageTitle = te(moduleLocale)
? t(moduleLocale) ? t(moduleLocale)
: t(`globals.pageTitles.${childPageTitle}`); : t(`globals.pageTitles.${childPageTitle}`);

View File

@ -1,3 +1,92 @@
import { RouterView } from 'vue-router'; import { RouterView } from 'vue-router';
export default {}; export default {
path: '/agency',
name: 'Agency',
meta: {
title: 'agency',
icon: 'garage_home',
moduleName: 'Agency',
},
component: RouterView,
redirect: { name: 'AgencyCard' },
menus: {
main: [],
card: ['AgencyBasicData', 'AgencyModes', 'AgencyWorkCenters', 'AgencyLog'],
},
children: [
{
path: '/agency/:id',
name: 'AgencyCard',
component: () => import('src/pages/Route/Agency/Card/AgencyCard.vue'),
redirect: { name: 'AgencySummary' },
children: [
{
name: 'AgencySummary',
path: 'summary',
meta: {
title: 'summary',
icon: 'view_list',
},
component: () =>
import('src/pages/Route/Agency/Card/AgencySummary.vue'),
},
{
name: 'AgencyBasicData',
path: 'basic-data',
meta: {
title: 'basicData',
icon: 'vn:settings',
},
component: () =>
import('pages/Route/Agency/Card/AgencyBasicData.vue'),
},
{
path: 'workCenter',
name: 'AgencyWorkCenterCard',
redirect: { name: 'AgencyWorkCenters' },
children: [
{
path: '',
name: 'AgencyWorkCenters',
meta: {
icon: 'apartment',
title: 'workCenters',
},
component: () =>
import(
'src/pages/Route/Agency/Card/AgencyWorkcenter.vue'
),
},
],
},
{
path: 'modes',
name: 'AgencyModesCard',
redirect: { name: 'AgencyModes' },
children: [
{
path: '',
name: 'AgencyModes',
meta: {
icon: 'format_list_bulleted',
title: 'modes',
},
component: () =>
import('src/pages/Route/Agency/Card/AgencyModes.vue'),
},
],
},
{
name: 'AgencyLog',
path: 'log',
meta: {
title: 'log',
icon: 'history',
},
component: () => import('src/pages/Route/Agency/Card/AgencyLog.vue'),
},
],
},
],
};

View File

@ -11,7 +11,7 @@ export default {
component: RouterView, component: RouterView,
redirect: { name: 'EntryMain' }, redirect: { name: 'EntryMain' },
menus: { menus: {
main: ['EntryList', 'EntryLatestBuys'], main: ['EntryList', 'MyEntries', 'EntryLatestBuys'],
card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryDms', 'EntryLog'], card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryDms', 'EntryLog'],
}, },
children: [ children: [
@ -30,6 +30,15 @@ export default {
}, },
component: () => import('src/pages/Entry/EntryList.vue'), component: () => import('src/pages/Entry/EntryList.vue'),
}, },
{
path: 'my',
name: 'MyEntries',
meta: {
title: 'labeler',
icon: 'sell',
},
component: () => import('src/pages/Entry/MyEntries.vue'),
},
{ {
path: 'create', path: 'create',
name: 'EntryCreate', name: 'EntryCreate',

View File

@ -201,15 +201,6 @@ export default {
}, },
component: () => import('src/pages/Item/Card/ItemLog.vue'), component: () => import('src/pages/Item/Card/ItemLog.vue'),
}, },
{
path: 'botanical',
name: 'ItemBotanical',
meta: {
title: 'botanical',
icon: 'vn:botanical',
},
component: () => import('src/pages/Item/Card/ItemBotanical.vue'),
},
], ],
}, },
], ],

View File

@ -11,7 +11,7 @@ export default {
component: RouterView, component: RouterView,
redirect: { name: 'RouteMain' }, redirect: { name: 'RouteMain' },
menus: { menus: {
main: ['RouteList', 'RouteAutonomous', 'RouteRoadmap', 'CmrList', 'Agency'], main: ['RouteList', 'RouteAutonomous', 'RouteRoadmap', 'CmrList', 'AgencyList'],
card: ['RouteBasicData', 'RouteTickets', 'RouteLog'], card: ['RouteBasicData', 'RouteTickets', 'RouteLog'],
}, },
children: [ children: [
@ -76,106 +76,18 @@ export default {
component: () => import('src/pages/Route/Cmr/CmrList.vue'), component: () => import('src/pages/Route/Cmr/CmrList.vue'),
}, },
{ {
path: 'agency', path: '/agency',
name: 'Agency',
meta: {
title: 'agency',
icon: 'garage_home',
moduleName: 'Agency',
},
redirect: { name: 'AgencyList' }, redirect: { name: 'AgencyList' },
menus: {
main: [],
card: [
'AgencyBasicData',
'AgencyModes',
'AgencyWorkCenters',
'AgencyLog',
],
},
children: [ children: [
{ {
path: '/agency', path: 'list',
name: 'AgencyCard', name: 'AgencyList',
meta: {
title: 'agencyList',
icon: 'view_list',
},
component: () => component: () =>
import('src/pages/Route/Agency/Card/AgencyCard.vue'), import('src/pages/Route/Agency/AgencyList.vue'),
redirect: { name: 'AgencySummary' },
children: [
{
name: 'AgencySummary',
path: 'summary',
meta: {
title: 'summary',
icon: 'view_list',
},
component: () =>
import(
'src/pages/Route/Agency/Card/AgencySummary.vue'
),
},
{
name: 'AgencyBasicData',
path: 'basic-data',
meta: {
title: 'basicData',
icon: 'vn:settings',
},
component: () =>
import(
'pages/Route/Agency/Card/AgencyBasicData.vue'
),
},
{
path: 'workCenter',
name: 'AgencyWorkCenterCard',
redirect: { name: 'AgencyWorkCenters' },
children: [
{
path: '',
name: 'AgencyWorkCenters',
meta: {
icon: 'apartment',
title: 'workCenters',
},
component: () =>
import(
'src/pages/Route/Agency/Card/AgencyWorkcenter.vue'
),
},
],
},
{
path: 'modes',
name: 'AgencyModesCard',
redirect: { name: 'AgencyModes' },
children: [
{
path: '',
name: 'AgencyModes',
meta: {
icon: 'format_list_bulleted',
title: 'modes',
},
component: () =>
import(
'src/pages/Route/Agency/Card/AgencyModes.vue'
),
},
],
},
{
name: 'AgencyLog',
path: 'log',
meta: {
title: 'log',
icon: 'history',
},
component: () =>
import(
'src/pages/Route/Agency/Card/AgencyLog.vue'
),
},
],
}, },
], ],
}, },

View File

@ -17,6 +17,7 @@ import order from 'src/router/modules/order';
import entry from 'src/router/modules/entry'; import entry from 'src/router/modules/entry';
import roadmap from 'src/router/modules/roadmap'; import roadmap from 'src/router/modules/roadmap';
import parking from 'src/router/modules/parking'; import parking from 'src/router/modules/parking';
import agency from 'src/router/modules/agency';
import zone from 'src/router/modules/zone'; import zone from 'src/router/modules/zone';
import account from './modules/account'; import account from './modules/account';
import monitor from 'src/router/modules/monitor'; import monitor from 'src/router/modules/monitor';
@ -78,6 +79,7 @@ const routes = [
roadmap, roadmap,
entry, entry,
parking, parking,
agency,
ItemType, ItemType,
zone, zone,
account, account,

View File

@ -60,7 +60,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
menuChildren = menuChildren.map(({ name, title, icon }) => ({ menuChildren = menuChildren.map(({ name, title, icon }) => ({
name, name,
icon, icon,
title: `${module}.pageTitles.${title}`, title: `globals.pageTitles.${title}`,
})); }));
if (meta && meta.roles && role.hasAny(meta.roles) === false) return; if (meta && meta.roles && role.hasAny(meta.roles) === false) return;
@ -70,7 +70,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
children: menuChildren, children: menuChildren,
}; };
if (meta) { if (meta) {
item.title = `${module}.pageTitles.${meta.title}`; item.title = `globals.pageTitles.${meta.title}`;
item.icon = meta.icon; item.icon = meta.icon;
} }

View File

@ -17,6 +17,7 @@ export const useStateStore = defineStore('stateStore', () => {
function toggleSubToolbar() { function toggleSubToolbar() {
subToolbar.value = !subToolbar.value; subToolbar.value = !subToolbar.value;
/* console.log('subToolbar.value: ', subToolbar.value); */
} }
function setMounted() { function setMounted() {

View File

@ -1,56 +0,0 @@
const locationOptions = '[role="listbox"] > div.q-virtual-scroll__content > .q-item';
describe('VnLocation', () => {
const dialogInputs = '.q-dialog label input';
describe('Worker Create', () => {
const inputLocation = '.q-form input[aria-label="Location"]';
beforeEach(() => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('/#/worker/create', { timeout: 5000 });
cy.waitForElement('.q-card');
});
it('Show all options', function () {
cy.get(inputLocation).click();
cy.get(locationOptions).should('have.length.at.least', 5);
});
it('input filter location as "al"', function () {
cy.get(inputLocation).click();
cy.get(inputLocation).clear();
cy.get(inputLocation).type('al');
cy.get(locationOptions).should('have.length.at.least', 3);
});
it('input filter location as "ecuador"', function () {
cy.get(inputLocation).click();
cy.get(inputLocation).clear();
cy.get(inputLocation).type('ecuador');
cy.get(locationOptions).should('have.length.at.least', 1);
});
});
describe('Fiscal-data', () => {
beforeEach(() => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('/#/supplier/567/fiscal-data', { timeout: 7000 });
cy.waitForElement('.q-form');
});
it('Create postCode', function () {
cy.get('.q-form > .q-card > .vn-row:nth-child(6) .--add-icon').click();
cy.get('.q-card > h1').should('have.text', 'New postcode');
cy.get(dialogInputs).eq(0).clear('12');
cy.get(dialogInputs).eq(0).type('1234453');
cy.selectOption(
'.q-dialog__inner > .column > #formModel > .q-card > :nth-child(4) > .q-select > .q-field__inner > .q-field__control ',
'Valencia'
);
cy.selectOption(
'.q-dialog__inner > .column > #formModel > .q-card > :nth-child(5) > .q-select > .q-field__inner > .q-field__control ',
'Province one'
);
cy.selectOption(
'.q-dialog__inner > .column > #formModel > .q-card > :nth-child(6) > .q-select > .q-field__inner > .q-field__control ',
'España'
);
cy.get('.q-mt-lg > .q-btn--standard').click();
});
});
});

View File

@ -0,0 +1,20 @@
describe('WagonTypeCreate', () => {
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('customer');
cy.visit(`/#/entry/my`, {
onBeforeLoad(win) {
cy.stub(win, 'open');
},
});
cy.waitForElement('.q-page', 6000);
});
it('should create edit and remove new dms', () => {
cy.get(
'[to="/null/2"] > .q-card > .column > .q-btn > .q-btn__content > .q-icon'
).click();
cy.get('.q-card__actions > .q-btn').click();
cy.window().its('open').should('be.called');
});
});

View File

@ -5,12 +5,11 @@ describe('InvoiceInList', () => {
':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content'; ':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content';
const firstDetailBtn = '.q-card:nth-child(1) .q-btn:nth-child(2)'; const firstDetailBtn = '.q-card:nth-child(1) .q-btn:nth-child(2)';
const summaryHeaders = '.summaryBody .header-link'; const summaryHeaders = '.summaryBody .header-link';
const screen = '.q-page-container > .q-drawer-container > .fullscreen';
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit(`/#/invoice-in/list`); cy.visit(`/#/invoice-in/list`);
cy.get(screen).click();
}); });
it('should redirect on clicking a invoice', () => { it('should redirect on clicking a invoice', () => {

View File

@ -8,9 +8,9 @@ describe('ParkingList', () => {
const summaryHeader = '.summaryBody .header'; const summaryHeader = '.summaryBody .header';
beforeEach(() => { beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit(`/#/parking/list`); cy.visit(`/#/parking/list`);
cy.closeSideMenu();
}); });
it('should redirect on clicking a parking', () => { it('should redirect on clicking a parking', () => {

View File

@ -1,21 +0,0 @@
/// <reference types="cypress" />
describe('VnBreadcrumbs', () => {
const firstCard = '.q-infinite-scroll > :nth-child(1)';
const lastBreadcrumb = '.q-breadcrumbs--last > .q-breadcrumbs__el';
beforeEach(() => {
cy.login('developer');
cy.visit('/');
});
it('should not be breadcrumbs', () => {
cy.get('.q-breadcrumbs').should('not.exist');
});
it('should get the correct breadcrumbs', () => {
cy.visit('#/customer/list');
cy.get('.q-breadcrumbs__el').should('have.length', 2);
cy.get(firstCard).click();
cy.get(`${lastBreadcrumb} > .q-icon`).should('have.text', 'launch');
});
});

View File

@ -1,25 +0,0 @@
/// <reference types="cypress" />
describe('VnLog', () => {
const chips = [
':nth-child(1) > :nth-child(1) > .q-item__label > .q-chip > .q-chip__content',
':nth-child(2) > :nth-child(1) > .q-item__label > .q-chip > .q-chip__content',
];
beforeEach(() => {
cy.login('developer');
cy.visit(`/#/claim/${1}/log`);
cy.openRightMenu();
});
// Se tiene que cambiar el Accept-Language a 'en', ya hay una tarea para eso #7189.
xit('should filter by insert actions', () => {
cy.checkOption(':nth-child(7) > .q-checkbox');
cy.get('.q-page').click();
cy.validateContent(chips[0], 'Document');
cy.validateContent(chips[1], 'Beginning');
});
xit('should filter by entity', () => {
cy.selectOption('.q-drawer--right .q-item > .q-select', 'Claim');
cy.get('.q-page').click();
cy.validateContent(chips[0], 'Claim');
});
});

View File

@ -1,36 +0,0 @@
/// <reference types="cypress" />
describe('VnSearchBar', () => {
const employeeId = ' #1';
const salesPersonId = ' #18';
const idGap = '.q-item > .q-item__label';
const vnTableRow = '.q-virtual-scroll__content';
beforeEach(() => {
cy.login('developer');
cy.visit('#/customer/list');
});
it('should redirect to customer summary page', () => {
searchAndCheck('1', employeeId);
searchAndCheck('salesPerson', salesPersonId);
});
it('should stay on the list page if there are several results or none', () => {
cy.writeSearchbar('salesA{enter}');
checkTableLength(2);
cy.clearSearchbar();
cy.writeSearchbar('0{enter}');
checkTableLength(0);
});
const searchAndCheck = (searchTerm, expectedText) => {
cy.clearSearchbar();
cy.writeSearchbar(`${searchTerm}{enter}`);
cy.get(idGap).should('have.text', expectedText);
};
const checkTableLength = (expectedLength) => {
cy.get(vnTableRow).find('tr').should('have.length', expectedLength);
};
});

View File

@ -50,8 +50,8 @@ Cypress.Commands.add('login', (user) => {
}); });
}); });
Cypress.Commands.add('waitForElement', (element) => { Cypress.Commands.add('waitForElement', (element, timeout = 5000) => {
cy.get(element, { timeout: 5000 }).should('be.visible'); cy.get(element, { timeout }).should('be.visible');
}); });
Cypress.Commands.add('getValue', (selector) => { Cypress.Commands.add('getValue', (selector) => {
@ -221,10 +221,6 @@ Cypress.Commands.add('openLeftMenu', (element) => {
if (element) cy.waitForElement(element); if (element) cy.waitForElement(element);
cy.get('.q-toolbar > .q-btn--round.q-btn--dense > .q-btn__content > .q-icon').click(); cy.get('.q-toolbar > .q-btn--round.q-btn--dense > .q-btn__content > .q-icon').click();
}); });
Cypress.Commands.add('closeSideMenu', (element) => {
if (element) cy.waitForElement(element);
cy.get('.fullscreen.q-drawer__backdrop:not(.hidden)').click();
});
Cypress.Commands.add('clearSearchbar', (element) => { Cypress.Commands.add('clearSearchbar', (element) => {
if (element) cy.waitForElement(element); if (element) cy.waitForElement(element);
@ -245,4 +241,3 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
Cypress.Commands.add('openActionsDescriptor', () => { Cypress.Commands.add('openActionsDescriptor', () => {
cy.get('.descriptor > .header > .q-btn').click(); cy.get('.descriptor > .header > .q-btn').click();
}); });
// registerCommands();

View File

@ -78,13 +78,13 @@ describe('Leftmenu', () => {
{ {
children: null, children: null,
name: 'CustomerList', name: 'CustomerList',
title: 'customer.pageTitles.list', title: 'globals.pageTitles.list',
icon: 'view_list', icon: 'view_list',
}, },
{ {
children: null, children: null,
name: 'CustomerCreate', name: 'CustomerCreate',
title: 'customer.pageTitles.createCustomer', title: 'globals.pageTitles.createCustomer',
icon: 'vn:addperson', icon: 'vn:addperson',
}, },
]; ];