forked from verdnatura/salix-front
Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 7113-fixClaimPhoto
This commit is contained in:
commit
f7e91424b9
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -5,14 +5,33 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2420.01]
|
||||||
|
|
||||||
|
## [2418.01]
|
||||||
|
|
||||||
|
## [2416.01] - 2024-04-18
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- (General) => Se vuelven a mostrar los parámetros en la url al aplicar un filtro
|
||||||
|
|
||||||
## [2414.01] - 2024-04-04
|
## [2414.01] - 2024-04-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- (Tickets) => Se añade la opción de clonar ticket. #6951
|
||||||
|
- (Parking) => Se añade la sección Parking. #5186
|
||||||
|
|
||||||
|
- (Rutas) => Se añade el campo "servida" a la tabla y se añade también a los filtros. #7130
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- (General) => Se corrige la redirección cuando hay 1 solo registro y cuando se aplica un filtro diferente al id al hacer una búsqueda general. #6893
|
||||||
|
|
||||||
## [2400.01] - 2024-01-04
|
## [2400.01] - 2024-01-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { defineConfig } = require('cypress');
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'http://localhost:9000/',
|
baseUrl: 'http://localhost:9000/',
|
||||||
|
experimentalStudio: true,
|
||||||
fixturesFolder: 'test/cypress/fixtures',
|
fixturesFolder: 'test/cypress/fixtures',
|
||||||
screenshotsFolder: 'test/cypress/screenshots',
|
screenshotsFolder: 'test/cypress/screenshots',
|
||||||
supportFile: 'test/cypress/support/index.js',
|
supportFile: 'test/cypress/support/index.js',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "24.14.0",
|
"version": "24.20.0",
|
||||||
"description": "Salix frontend",
|
"description": "Salix frontend",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Verdnatura",
|
"author": "Verdnatura",
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||||
"@pinia/testing": "^0.1.2",
|
"@pinia/testing": "^0.1.2",
|
||||||
"@quasar/app-vite": "^1.7.3",
|
"@quasar/app-vite": "^1.7.3",
|
||||||
|
"@quasar/quasar-app-extension-qcalendar": "4.0.0-beta.15",
|
||||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0",
|
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0",
|
||||||
"@vue/test-utils": "^2.4.4",
|
"@vue/test-utils": "^2.4.4",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
|
|
4178
pnpm-lock.yaml
4178
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -29,7 +29,7 @@ module.exports = configure(function (/* ctx */) {
|
||||||
// app boot file (/src/boot)
|
// app boot file (/src/boot)
|
||||||
// --> boot files are part of "main.js"
|
// --> boot files are part of "main.js"
|
||||||
// https://v2.quasar.dev/quasar-cli/boot-files
|
// https://v2.quasar.dev/quasar-cli/boot-files
|
||||||
boot: ['i18n', 'axios', 'vnDate', 'validations'],
|
boot: ['i18n', 'axios', 'vnDate', 'validations', 'quasar.defaults'],
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
css: ['app.scss'],
|
css: ['app.scss'],
|
||||||
|
@ -93,13 +93,11 @@ module.exports = configure(function (/* ctx */) {
|
||||||
[
|
[
|
||||||
VueI18nPlugin({
|
VueI18nPlugin({
|
||||||
runtimeOnly: false,
|
runtimeOnly: false,
|
||||||
|
include: [
|
||||||
|
path.resolve(__dirname, './src/i18n/locale/**'),
|
||||||
|
path.resolve(__dirname, './src/pages/**/locale/**'),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
{
|
|
||||||
// if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
|
|
||||||
// compositionOnly: false,
|
|
||||||
// you need to set i18n resource including paths !
|
|
||||||
include: path.resolve(__dirname, './src/i18n/**'),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -117,6 +115,7 @@ module.exports = configure(function (/* ctx */) {
|
||||||
secure: false,
|
secure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
open: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"@quasar/testing-unit-vitest": {
|
"@quasar/testing-unit-vitest": {
|
||||||
"options": [
|
"options": ["scripts"]
|
||||||
"scripts"
|
},
|
||||||
]
|
"@quasar/qcalendar": {}
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ onMounted(() => {
|
||||||
if (availableLocales.includes(userLang)) {
|
if (availableLocales.includes(userLang)) {
|
||||||
locale.value = userLang;
|
locale.value = userLang;
|
||||||
} else {
|
} else {
|
||||||
locale.value = fallbackLocale;
|
locale.value = fallbackLocale.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { QTable } from 'quasar';
|
||||||
|
import setDefault from './setDefault';
|
||||||
|
|
||||||
|
setDefault(QTable, 'pagination', { rowsPerPage: 0 });
|
||||||
|
setDefault(QTable, 'hidePagination', true);
|
|
@ -0,0 +1,18 @@
|
||||||
|
export default function (component, key, value) {
|
||||||
|
const prop = component.props[key];
|
||||||
|
switch (typeof prop) {
|
||||||
|
case 'object':
|
||||||
|
prop.default = value;
|
||||||
|
break;
|
||||||
|
case 'function':
|
||||||
|
component.props[key] = {
|
||||||
|
type: prop,
|
||||||
|
default: value,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'undefined':
|
||||||
|
throw new Error('unknown prop: ' + key);
|
||||||
|
default:
|
||||||
|
throw new Error('unhandled type: ' + typeof prop);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { getCurrentInstance } from 'vue';
|
||||||
|
|
||||||
|
const filterAvailableInput = element => element.classList.contains('q-field__native') && !element.disabled
|
||||||
|
const filterAvailableText = element => element.__vueParentComponent.type.name === 'QInput' && element.__vueParentComponent?.attrs?.class !== 'vn-input-date';
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mounted: function () {
|
||||||
|
const vm = getCurrentInstance();
|
||||||
|
if (vm.type.name === 'QForm')
|
||||||
|
if (!['searchbarForm','filterPanelForm'].includes(this.$el?.id)) {
|
||||||
|
// AUTOFOCUS
|
||||||
|
const elementsArray = Array.from(this.$el.elements);
|
||||||
|
const firstInputElement = elementsArray.filter(filterAvailableInput).find(filterAvailableText);
|
||||||
|
|
||||||
|
if (firstInputElement) {
|
||||||
|
firstInputElement.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './defaults/qTable';
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { boot } from 'quasar/wrappers';
|
||||||
|
import qFormMixin from './qformMixin';
|
||||||
|
|
||||||
|
export default boot(({ app }) => {
|
||||||
|
app.mixin(qFormMixin);
|
||||||
|
});
|
|
@ -3,7 +3,7 @@ import { reactive, ref, onMounted, nextTick } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
@ -78,7 +78,7 @@ onMounted(async () => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('country')"
|
:label="t('country')"
|
||||||
v-model="data.countryFk"
|
v-model="data.countryFk"
|
||||||
:options="countriesOptions"
|
:options="countriesOptions"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
import VnInputDate from './common/VnInputDate.vue';
|
import VnInputDate from './common/VnInputDate.vue';
|
||||||
|
@ -73,7 +73,7 @@ const onDataSaved = async (formData, requestResponse) => {
|
||||||
</span>
|
</span>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Ticket')"
|
:label="t('Ticket')"
|
||||||
:options="ticketsOptions"
|
:options="ticketsOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -92,13 +92,13 @@ const onDataSaved = async (formData, requestResponse) => {
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelect>
|
||||||
</div>
|
</div>
|
||||||
<span class="row items-center" style="max-width: max-content">{{
|
<span class="row items-center" style="max-width: max-content">{{
|
||||||
t('Or')
|
t('Or')
|
||||||
}}</span>
|
}}</span>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Client')"
|
:label="t('Client')"
|
||||||
:options="clientsOptions"
|
:options="clientsOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -114,7 +114,7 @@ const onDataSaved = async (formData, requestResponse) => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Serial')"
|
:label="t('Serial')"
|
||||||
:options="invoiceOutSerialsOptions"
|
:options="invoiceOutSerialsOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -125,7 +125,7 @@ const onDataSaved = async (formData, requestResponse) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Area')"
|
:label="t('Area')"
|
||||||
:options="taxAreasOptions"
|
:options="taxAreasOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Province')"
|
:label="t('Province')"
|
||||||
:options="provincesOptions"
|
:options="provincesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import CreateNewCityForm from './CreateNewCityForm.vue';
|
import CreateNewCityForm from './CreateNewCityForm.vue';
|
||||||
import CreateNewProvinceForm from './CreateNewProvinceForm.vue';
|
import CreateNewProvinceForm from './CreateNewProvinceForm.vue';
|
||||||
|
@ -28,8 +28,23 @@ const countriesOptions = ref([]);
|
||||||
const provincesOptions = ref([]);
|
const provincesOptions = ref([]);
|
||||||
const townsLocationOptions = ref([]);
|
const townsLocationOptions = ref([]);
|
||||||
|
|
||||||
const onDataSaved = (dataSaved) => {
|
const onDataSaved = (formData) => {
|
||||||
emit('onDataSaved', dataSaved);
|
const newPostcode = {
|
||||||
|
...formData
|
||||||
|
};
|
||||||
|
const townObject = townsLocationOptions.value.find(
|
||||||
|
({id}) => id === formData.townFk
|
||||||
|
);
|
||||||
|
newPostcode.town = townObject?.name;
|
||||||
|
const provinceObject = provincesOptions.value.find(
|
||||||
|
({id}) => id === formData.provinceFk
|
||||||
|
);
|
||||||
|
newPostcode.province = provinceObject?.name;
|
||||||
|
const countryObject = countriesOptions.value.find(
|
||||||
|
({id}) => id === formData.countryFk
|
||||||
|
);
|
||||||
|
newPostcode.country = countryObject?.country;
|
||||||
|
emit('onDataSaved', newPostcode);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCityCreated = async ({ name, provinceFk }, formData) => {
|
const onCityCreated = async ({ name, provinceFk }, formData) => {
|
||||||
|
@ -73,7 +88,7 @@ const onProvinceCreated = async ({ name }, formData) => {
|
||||||
:title="t('New postcode')"
|
:title="t('New postcode')"
|
||||||
:subtitle="t('Please, ensure you put the correct data!')"
|
:subtitle="t('Please, ensure you put the correct data!')"
|
||||||
:form-initial-data="postcodeFormData"
|
:form-initial-data="postcodeFormData"
|
||||||
@on-data-saved="onDataSaved($event)"
|
@on-data-saved="onDataSaved"
|
||||||
>
|
>
|
||||||
<template #form-inputs="{ data, validate }">
|
<template #form-inputs="{ data, validate }">
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
@ -123,7 +138,7 @@ const onProvinceCreated = async ({ name }, formData) => {
|
||||||
</VnSelectDialog>
|
</VnSelectDialog>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Country')"
|
:label="t('Country')"
|
||||||
:options="countriesOptions"
|
:options="countriesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Autonomy')"
|
:label="t('Autonomy')"
|
||||||
:options="autonomiesOptions"
|
:options="autonomiesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Model')"
|
:label="t('Model')"
|
||||||
:options="thermographsModels"
|
:options="thermographsModels"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -78,7 +78,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-xl">
|
<VnRow class="row q-gutter-md q-mb-xl">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Warehouse')"
|
:label="t('Warehouse')"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -89,7 +89,7 @@ const onDataSaved = (dataSaved) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Temperature')"
|
:label="t('Temperature')"
|
||||||
:options="temperaturesOptions"
|
:options="temperaturesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -24,6 +24,10 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
limit: {
|
||||||
|
type: Number,
|
||||||
|
default: 20,
|
||||||
|
},
|
||||||
saveUrl: {
|
saveUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -76,6 +80,7 @@ defineExpose({
|
||||||
reset,
|
reset,
|
||||||
hasChanges,
|
hasChanges,
|
||||||
saveChanges,
|
saveChanges,
|
||||||
|
getChanges,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function fetch(data) {
|
async function fetch(data) {
|
||||||
|
@ -119,11 +124,16 @@ async function onSubmit() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await saveChanges();
|
await saveChanges($props.saveFn ? formData.value : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveChanges(data) {
|
async function saveChanges(data) {
|
||||||
if ($props.saveFn) return $props.saveFn(data, getChanges);
|
if ($props.saveFn) {
|
||||||
|
$props.saveFn(data, getChanges);
|
||||||
|
isLoading.value = false;
|
||||||
|
hasChanges.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const changes = data || getChanges();
|
const changes = data || getChanges();
|
||||||
try {
|
try {
|
||||||
await axios.post($props.saveUrl || $props.url + '/crud', changes);
|
await axios.post($props.saveUrl || $props.url + '/crud', changes);
|
||||||
|
@ -260,6 +270,7 @@ watch(formUrl, async () => {
|
||||||
<template>
|
<template>
|
||||||
<VnPaginate
|
<VnPaginate
|
||||||
:url="url"
|
:url="url"
|
||||||
|
:limit="limit"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@on-fetch="fetch"
|
@on-fetch="fetch"
|
||||||
:skeleton="false"
|
:skeleton="false"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { reactive, computed, ref } from 'vue';
|
import { reactive, computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
@ -293,7 +293,7 @@ const makeRequest = async () => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Orientation')"
|
:label="t('Orientation')"
|
||||||
:options="viewportTypes"
|
:options="viewportTypes"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive } from 'vue';
|
import { ref, markRaw } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import { QCheckbox } from 'quasar';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
@ -28,11 +31,16 @@ const $props = defineProps({
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
|
||||||
const formData = reactive({
|
const inputs = {
|
||||||
field: null,
|
input: markRaw(VnInput),
|
||||||
newValue: null,
|
number: markRaw(VnInput),
|
||||||
});
|
date: markRaw(VnInputDate),
|
||||||
|
checkbox: markRaw(QCheckbox),
|
||||||
|
select: markRaw(VnSelect),
|
||||||
|
};
|
||||||
|
|
||||||
|
const newValue = ref(null);
|
||||||
|
const selectedField = ref(null);
|
||||||
const closeButton = ref(null);
|
const closeButton = ref(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
@ -47,8 +55,8 @@ const submitData = async () => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
const rowsToEdit = $props.rows.map((row) => ({ id: row.id, itemFk: row.itemFk }));
|
const rowsToEdit = $props.rows.map((row) => ({ id: row.id, itemFk: row.itemFk }));
|
||||||
const payload = {
|
const payload = {
|
||||||
field: formData.field,
|
field: selectedField.value.field,
|
||||||
newValue: formData.newValue,
|
newValue: newValue.value,
|
||||||
lines: rowsToEdit,
|
lines: rowsToEdit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,19 +83,20 @@ const closeForm = () => {
|
||||||
<span class="countLines">{{ ` ${rows.length} ` }}</span>
|
<span class="countLines">{{ ` ${rows.length} ` }}</span>
|
||||||
<span class="title">{{ t('buy(s)') }}</span>
|
<span class="title">{{ t('buy(s)') }}</span>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<VnSelect
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('Field to edit')"
|
:label="t('Field to edit')"
|
||||||
:options="fieldsOptions"
|
:options="fieldsOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
option-label="label"
|
option-label="label"
|
||||||
option-value="field"
|
v-model="selectedField"
|
||||||
v-model="formData.field"
|
/>
|
||||||
|
<component
|
||||||
|
:is="inputs[selectedField?.component || 'input']"
|
||||||
|
v-bind="selectedField?.attrs || {}"
|
||||||
|
v-model="newValue"
|
||||||
|
:label="t('Value')"
|
||||||
|
style="width: 200px"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnInput :label="t('Value')" v-model="formData.newValue" />
|
|
||||||
</div>
|
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<div class="q-mt-lg row justify-end">
|
<div class="q-mt-lg row justify-end">
|
||||||
<QBtn
|
<QBtn
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { h, onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -60,3 +60,6 @@ async function fetch(fetchFilter = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<template>
|
||||||
|
<template></template>
|
||||||
|
</template>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useRoute } from 'vue-router';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -160,7 +160,7 @@ const selectItem = ({ id }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('entry.buys.producer')"
|
:label="t('entry.buys.producer')"
|
||||||
:options="producersOptions"
|
:options="producersOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -170,7 +170,7 @@ const selectItem = ({ id }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('entry.buys.type')"
|
:label="t('entry.buys.type')"
|
||||||
:options="ItemTypesOptions"
|
:options="ItemTypesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -180,7 +180,7 @@ const selectItem = ({ id }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('entry.buys.color')"
|
:label="t('entry.buys.color')"
|
||||||
:options="InksOptions"
|
:options="InksOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -202,7 +202,6 @@ const selectItem = ({ id }) => {
|
||||||
<QTable
|
<QTable
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
:rows="tableRows"
|
:rows="tableRows"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:hide-header="!tableRows || !tableRows.length > 0"
|
:hide-header="!tableRows || !tableRows.length > 0"
|
||||||
:no-data-label="t('Enter a new search')"
|
:no-data-label="t('Enter a new search')"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -146,7 +146,7 @@ const selectTravel = ({ id }) => {
|
||||||
<h1 class="title">{{ t('Filter travels') }}</h1>
|
<h1 class="title">{{ t('Filter travels') }}</h1>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('entry.basicData.agency')"
|
:label="t('entry.basicData.agency')"
|
||||||
:options="agenciesOptions"
|
:options="agenciesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -156,7 +156,7 @@ const selectTravel = ({ id }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('entry.basicData.warehouseOut')"
|
:label="t('entry.basicData.warehouseOut')"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -166,7 +166,7 @@ const selectTravel = ({ id }) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('entry.basicData.warehouseIn')"
|
:label="t('entry.basicData.warehouseIn')"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -200,7 +200,6 @@ const selectTravel = ({ id }) => {
|
||||||
<QTable
|
<QTable
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
:rows="tableRows"
|
:rows="tableRows"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:hide-header="!tableRows || !tableRows.length > 0"
|
:hide-header="!tableRows || !tableRows.length > 0"
|
||||||
:no-data-label="t('Enter a new search')"
|
:no-data-label="t('Enter a new search')"
|
||||||
|
|
|
@ -81,6 +81,7 @@ const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||||
const componentIsRendered = ref(false);
|
const componentIsRendered = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
originalData.value = $props.formInitialData;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
componentIsRendered.value = true;
|
componentIsRendered.value = true;
|
||||||
});
|
});
|
||||||
|
@ -101,8 +102,7 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (!hasChanges.value) next();
|
if (hasChanges.value && $props.observeFormChanges)
|
||||||
|
|
||||||
quasar.dialog({
|
quasar.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
@ -111,6 +111,7 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
promise: () => next(),
|
promise: () => next(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
else next();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -126,18 +127,18 @@ const isLoading = ref(false);
|
||||||
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
|
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
|
||||||
const isResetting = ref(false);
|
const isResetting = ref(false);
|
||||||
const hasChanges = ref(!$props.observeFormChanges);
|
const hasChanges = ref(!$props.observeFormChanges);
|
||||||
const originalData = ref({ ...$props.formInitialData });
|
const originalData = ref({});
|
||||||
const formData = computed(() => state.get($props.model));
|
const formData = computed(() => state.get($props.model));
|
||||||
const formUrl = computed(() => $props.url);
|
const formUrl = computed(() => $props.url);
|
||||||
const defaultButtons = computed(() => ({
|
const defaultButtons = computed(() => ({
|
||||||
save: {
|
save: {
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
icon: 'restart_alt',
|
icon: 'save',
|
||||||
label: 'globals.save',
|
label: 'globals.save',
|
||||||
},
|
},
|
||||||
reset: {
|
reset: {
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
icon: 'save',
|
icon: 'restart_alt',
|
||||||
label: 'globals.reset',
|
label: 'globals.reset',
|
||||||
},
|
},
|
||||||
...$props.defaultButtons,
|
...$props.defaultButtons,
|
||||||
|
@ -154,14 +155,18 @@ const startFormWatcher = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
|
try {
|
||||||
const { data } = await axios.get($props.url, {
|
const { data } = await axios.get($props.url, {
|
||||||
params: { filter: JSON.stringify($props.filter) },
|
params: { filter: JSON.stringify($props.filter) },
|
||||||
});
|
});
|
||||||
|
|
||||||
state.set($props.model, data);
|
state.set($props.model, data);
|
||||||
originalData.value = data && JSON.parse(JSON.stringify(data));
|
originalData.value = data && JSON.parse(JSON.stringify(data));
|
||||||
|
|
||||||
emit('onFetch', state.get($props.model));
|
emit('onFetch', state.get($props.model));
|
||||||
|
} catch (error) {
|
||||||
|
state.set($props.model, {});
|
||||||
|
originalData.value = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
@ -227,6 +232,7 @@ watch(formUrl, async () => {
|
||||||
defineExpose({
|
defineExpose({
|
||||||
save,
|
save,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
hasChanges,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -284,6 +290,9 @@ defineExpose({
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.q-notifications {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
#formModel {
|
#formModel {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -76,23 +76,25 @@ defineExpose({
|
||||||
<p>{{ subtitle }}</p>
|
<p>{{ subtitle }}</p>
|
||||||
<slot name="form-inputs" :data="data" :validate="validate" />
|
<slot name="form-inputs" :data="data" :validate="validate" />
|
||||||
<div class="q-mt-lg row justify-end">
|
<div class="q-mt-lg row justify-end">
|
||||||
<QBtn
|
|
||||||
:label="t('globals.save')"
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
:disabled="isLoading"
|
|
||||||
:loading="isLoading"
|
|
||||||
/>
|
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('globals.cancel')"
|
:label="t('globals.cancel')"
|
||||||
|
:title="t('globals.cancel')"
|
||||||
type="reset"
|
type="reset"
|
||||||
color="primary"
|
color="primary"
|
||||||
flat
|
flat
|
||||||
class="q-ml-sm"
|
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
v-close-popup
|
v-close-popup
|
||||||
/>
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.save')"
|
||||||
|
:title="t('globals.save')"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
class="q-ml-sm"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</FormModel>
|
</FormModel>
|
||||||
|
|
|
@ -56,14 +56,6 @@ const closeForm = () => {
|
||||||
<p>{{ subtitle }}</p>
|
<p>{{ subtitle }}</p>
|
||||||
<slot name="form-inputs" />
|
<slot name="form-inputs" />
|
||||||
<div class="q-mt-lg row justify-end">
|
<div class="q-mt-lg row justify-end">
|
||||||
<QBtn
|
|
||||||
v-if="defaultSubmitButton"
|
|
||||||
:label="customSubmitButtonLabel || t('globals.save')"
|
|
||||||
type="submit"
|
|
||||||
color="primary"
|
|
||||||
:disabled="isLoading"
|
|
||||||
:loading="isLoading"
|
|
||||||
/>
|
|
||||||
<QBtn
|
<QBtn
|
||||||
v-if="defaultCancelButton"
|
v-if="defaultCancelButton"
|
||||||
:label="t('globals.cancel')"
|
:label="t('globals.cancel')"
|
||||||
|
@ -74,6 +66,14 @@ const closeForm = () => {
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
v-close-popup
|
v-close-popup
|
||||||
/>
|
/>
|
||||||
|
<QBtn
|
||||||
|
v-if="defaultSubmitButton"
|
||||||
|
:label="customSubmitButtonLabel || t('globals.save')"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
|
/>
|
||||||
<slot name="customButtons" />
|
<slot name="customButtons" />
|
||||||
</div>
|
</div>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
|
@ -0,0 +1,359 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
|
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
customTags: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
exprBuilder: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const itemCategories = ref([]);
|
||||||
|
const selectedCategoryFk = ref(null);
|
||||||
|
const selectedTypeFk = ref(null);
|
||||||
|
const itemTypesOptions = ref([]);
|
||||||
|
const suppliersOptions = ref([]);
|
||||||
|
const tagOptions = ref([]);
|
||||||
|
const tagValues = ref([]);
|
||||||
|
|
||||||
|
const categoryList = computed(() => {
|
||||||
|
return (itemCategories.value || [])
|
||||||
|
.filter((category) => category.display)
|
||||||
|
.map((category) => ({
|
||||||
|
...category,
|
||||||
|
icon: `vn:${(category.icon || '').split('-')[1]}`,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedCategory = computed(() =>
|
||||||
|
(itemCategories.value || []).find(
|
||||||
|
(category) => category?.id === selectedCategoryFk.value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedType = computed(() => {
|
||||||
|
return (itemTypesOptions.value || []).find(
|
||||||
|
(type) => type?.id === selectedTypeFk.value
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectCategory = async (params, categoryId, search) => {
|
||||||
|
if (params.categoryFk === categoryId) {
|
||||||
|
resetCategory(params);
|
||||||
|
search();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedCategoryFk.value = categoryId;
|
||||||
|
params.categoryFk = categoryId;
|
||||||
|
await fetchItemTypes(categoryId);
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetCategory = (params) => {
|
||||||
|
selectedCategoryFk.value = null;
|
||||||
|
itemTypesOptions.value = null;
|
||||||
|
if (params) {
|
||||||
|
params.categoryFk = null;
|
||||||
|
params.typeFk = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyTags = (params, search) => {
|
||||||
|
params.tags = tagValues.value
|
||||||
|
.filter((tag) => tag.selectedTag && tag.value)
|
||||||
|
.map((tag) => ({
|
||||||
|
tagFk: tag.selectedTag.id,
|
||||||
|
tagName: tag.selectedTag.name,
|
||||||
|
value: tag.value,
|
||||||
|
}));
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchItemTypes = async (id) => {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
fields: ['id', 'name', 'categoryFk'],
|
||||||
|
where: { categoryFk: id },
|
||||||
|
include: 'category',
|
||||||
|
order: 'name ASC',
|
||||||
|
};
|
||||||
|
const { data } = await axios.get('ItemTypes', {
|
||||||
|
params: { filter: JSON.stringify(filter) },
|
||||||
|
});
|
||||||
|
itemTypesOptions.value = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching item types', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCategoryClass = (category, params) => {
|
||||||
|
if (category.id === params?.categoryFk) {
|
||||||
|
return 'active';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedTagValues = async (tag) => {
|
||||||
|
try {
|
||||||
|
tag.value = null;
|
||||||
|
const filter = {
|
||||||
|
fields: ['value'],
|
||||||
|
order: 'value ASC',
|
||||||
|
limit: 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = { filter: JSON.stringify(filter) };
|
||||||
|
const { data } = await axios.get(`Tags/${tag.selectedTag.id}/filterValue`, {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
tag.valueOptions = data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error getting selected tag values');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTag = (index, params, search) => {
|
||||||
|
(tagValues.value || []).splice(index, 1);
|
||||||
|
applyTags(params, search);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="ItemCategories"
|
||||||
|
limit="30"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (itemCategories = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Suppliers"
|
||||||
|
limit="30"
|
||||||
|
auto-load
|
||||||
|
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
|
||||||
|
@on-fetch="(data) => (suppliersOptions = data)"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Tags"
|
||||||
|
:filter="{ fields: ['id', 'name', 'isFree'] }"
|
||||||
|
auto-load
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (tagOptions = data)"
|
||||||
|
/>
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:expr-builder="exprBuilder"
|
||||||
|
:custom-tags="customTags"
|
||||||
|
>
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<strong v-if="tag.label === 'categoryFk'">
|
||||||
|
{{ t(selectedCategory?.name || '') }}
|
||||||
|
</strong>
|
||||||
|
<strong v-else-if="tag.label === 'typeFk'">
|
||||||
|
{{ t(selectedType?.name || '') }}
|
||||||
|
</strong>
|
||||||
|
<div v-else class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`components.itemsFilterPanel.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #customTags="{ tags, params }">
|
||||||
|
<template v-for="tag in tags" :key="tag.label">
|
||||||
|
<VnFilterPanelChip
|
||||||
|
v-for="chip in tag.value"
|
||||||
|
:key="chip"
|
||||||
|
removable
|
||||||
|
@remove="removeTagChip(chip, params, searchFn)"
|
||||||
|
>
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ chip.tagName }}: </strong>
|
||||||
|
<span>"{{ chip.value }}"</span>
|
||||||
|
</div>
|
||||||
|
</VnFilterPanelChip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<QItem class="category-filter q-mt-md">
|
||||||
|
<QBtn
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
v-for="category in categoryList"
|
||||||
|
:key="category.name"
|
||||||
|
:class="['category', getCategoryClass(category, params)]"
|
||||||
|
:icon="category.icon"
|
||||||
|
@click="selectCategory(params, category.id, searchFn)"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t(category.name) }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-md">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.itemsFilterPanel.typeFk')"
|
||||||
|
v-model="params.typeFk"
|
||||||
|
:options="itemTypesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
use-input
|
||||||
|
:disable="!selectedCategoryFk"
|
||||||
|
@update:model-value="
|
||||||
|
(value) => {
|
||||||
|
selectedTypeFk = value;
|
||||||
|
searchFn();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ opt.categoryName }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelect>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QSeparator />
|
||||||
|
<slot name="body" :params="params" :search-fn="searchFn" />
|
||||||
|
<QItem
|
||||||
|
v-for="(value, index) in tagValues"
|
||||||
|
:key="value"
|
||||||
|
class="q-mt-md filter-value"
|
||||||
|
>
|
||||||
|
<QItemSection class="col">
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.itemsFilterPanel.tag')"
|
||||||
|
v-model="value.selectedTag"
|
||||||
|
:options="tagOptions"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
:emit-value="false"
|
||||||
|
use-input
|
||||||
|
:is-clearable="false"
|
||||||
|
@update:model-value="getSelectedTagValues(value)"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection class="col">
|
||||||
|
<VnSelect
|
||||||
|
v-if="!value?.selectedTag?.isFree && value.valueOptions"
|
||||||
|
:label="t('components.itemsFilterPanel.value')"
|
||||||
|
v-model="value.value"
|
||||||
|
:options="value.valueOptions || []"
|
||||||
|
option-value="value"
|
||||||
|
option-label="value"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
use-input
|
||||||
|
:disable="!value"
|
||||||
|
:is-clearable="false"
|
||||||
|
@update:model-value="applyTags(params, searchFn)"
|
||||||
|
/>
|
||||||
|
<VnInput
|
||||||
|
v-else
|
||||||
|
v-model="value.value"
|
||||||
|
:label="t('components.itemsFilterPanel.value')"
|
||||||
|
:disable="!value"
|
||||||
|
is-outlined
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="applyTags(params, searchFn)"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
class="fill-icon-on-hover q-px-xs"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
@click="removeTag(index, params, searchFn)"
|
||||||
|
/>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-mt-lg">
|
||||||
|
<QIcon
|
||||||
|
name="add_circle"
|
||||||
|
class="fill-icon-on-hover q-px-xs"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
@click="tagValues.push({})"
|
||||||
|
/>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.category-filter {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.category {
|
||||||
|
padding: 8px;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
background-color: var(--vn-accent-color);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-value {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
supplier: Supplier
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
active: Is active
|
||||||
|
visible: Is visible
|
||||||
|
floramondo: Is floramondo
|
||||||
|
salesPersonFk: Buyer
|
||||||
|
categoryFk: Category
|
||||||
|
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
supplier: Proveedor
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
active: Activo
|
||||||
|
visible: Visible
|
||||||
|
floramondo: Floramondo
|
||||||
|
salesPersonFk: Comprador
|
||||||
|
categoryFk: Categoría
|
||||||
|
</i18n>
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref, reactive } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QSeparator, useQuasar } from 'quasar';
|
import { QSeparator, useQuasar } from 'quasar';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
@ -22,6 +22,8 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const expansionItemElements = reactive({});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await navigation.fetchPinned();
|
await navigation.fetchPinned();
|
||||||
getRoutes();
|
getRoutes();
|
||||||
|
@ -108,6 +110,10 @@ async function togglePinned(item, event) {
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleItemExpansion = (itemName) => {
|
||||||
|
expansionItemElements[itemName].scrollToLastElement();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -213,8 +219,12 @@ async function togglePinned(item, event) {
|
||||||
:icon="item.icon"
|
:icon="item.icon"
|
||||||
:label="t(item.title)"
|
:label="t(item.title)"
|
||||||
:content-inset-level="0.5"
|
:content-inset-level="0.5"
|
||||||
|
@after-show="handleItemExpansion(item.name)"
|
||||||
>
|
>
|
||||||
<LeftMenuItemGroup :item="item" />
|
<LeftMenuItemGroup
|
||||||
|
:ref="(el) => (expansionItemElements[item.name] = el)"
|
||||||
|
:item="item"
|
||||||
|
/>
|
||||||
</QExpansionItem>
|
</QExpansionItem>
|
||||||
</QList>
|
</QList>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t, te } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
|
@ -11,19 +11,25 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const item = computed(() => props.item); // eslint-disable-line vue/no-dupe-keys
|
const itemComputed = computed(() => {
|
||||||
|
const item = JSON.parse(JSON.stringify(props.item));
|
||||||
|
const [, , section] = item.title.split('.');
|
||||||
|
|
||||||
|
if (!te(item.title)) item.title = t(`globals.pageTitles.${section}`);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QItem active-class="text-primary" :to="{ name: item.name }" clickable v-ripple>
|
<QItem active-class="bg-hover" :to="{ name: itemComputed.name }" clickable v-ripple>
|
||||||
<QItemSection avatar v-if="item.icon">
|
<QItemSection avatar v-if="itemComputed.icon">
|
||||||
<QIcon :name="item.icon" />
|
<QIcon :name="itemComputed.icon" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection avatar v-if="!item.icon">
|
<QItemSection avatar v-if="!itemComputed.icon">
|
||||||
<QIcon name="disabled_by_default" />
|
<QIcon name="disabled_by_default" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection>{{ t(item.title) }}</QItemSection>
|
<QItemSection>{{ t(itemComputed.title) }}</QItemSection>
|
||||||
<QItemSection side>
|
<QItemSection side>
|
||||||
<slot name="side" :item="item" />
|
<slot name="side" :item="itemComputed" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import LeftMenuItem from './LeftMenuItem.vue';
|
import LeftMenuItem from './LeftMenuItem.vue';
|
||||||
|
import { elementIsVisibleInViewport } from 'src/composables/elementIsVisibleInViewport';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
|
@ -13,10 +14,27 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const groupEnd = ref(null);
|
||||||
|
|
||||||
|
const scrollToLastElement = () => {
|
||||||
|
if (groupEnd.value && !elementIsVisibleInViewport(groupEnd.value)) {
|
||||||
|
groupEnd.value.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'end',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const item = computed(() => props.item); // eslint-disable-line vue/no-dupe-keys
|
const item = computed(() => props.item); // eslint-disable-line vue/no-dupe-keys
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
scrollToLastElement,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-for="section in item.children" :key="section.name">
|
<template v-for="section in item.children" :key="section.name">
|
||||||
<LeftMenuItem :item="section" />
|
<LeftMenuItem :item="section" />
|
||||||
</template>
|
</template>
|
||||||
|
<div ref="groupEnd" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FormModelPopup from './FormModelPopup.vue';
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
@ -54,12 +54,13 @@ const onDataSaved = (data) => {
|
||||||
<QInput
|
<QInput
|
||||||
:label="t('Type the visible quantity')"
|
:label="t('Type the visible quantity')"
|
||||||
v-model.number="data.quantity"
|
v-model.number="data.quantity"
|
||||||
|
autofocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Warehouse')"
|
:label="t('Warehouse')"
|
||||||
v-model="data.warehouseFk"
|
v-model="data.warehouseFk"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import FormPopup from './FormPopup.vue';
|
import FormPopup from './FormPopup.vue';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -84,7 +84,7 @@ const transferInvoice = async () => {
|
||||||
<template #form-inputs>
|
<template #form-inputs>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Client')"
|
:label="t('Client')"
|
||||||
:options="clientsOptions"
|
:options="clientsOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -103,10 +103,10 @@ const transferInvoice = async () => {
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelect>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Rectificative type')"
|
:label="t('Rectificative type')"
|
||||||
:options="rectificativeTypeOptions"
|
:options="rectificativeTypeOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -119,7 +119,7 @@ const transferInvoice = async () => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Class')"
|
:label="t('Class')"
|
||||||
:options="siiTypeInvoiceOutsOptions"
|
:options="siiTypeInvoiceOutsOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
@ -138,10 +138,10 @@ const transferInvoice = async () => {
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelect>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Type')"
|
:label="t('Type')"
|
||||||
:options="invoiceCorrectionTypesOptions"
|
:options="invoiceCorrectionTypesOptions"
|
||||||
hide-selected
|
hide-selected
|
||||||
|
|
|
@ -7,12 +7,16 @@ import axios from 'axios';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { localeEquivalence } from 'src/i18n/index';
|
import { localeEquivalence } from 'src/i18n/index';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
|
||||||
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();
|
||||||
import { useClipboard } from 'src/composables/useClipboard';
|
import { useClipboard } from 'src/composables/useClipboard';
|
||||||
|
import { ref } from 'vue';
|
||||||
const { copyText } = useClipboard();
|
const { copyText } = useClipboard();
|
||||||
const userLocale = computed({
|
const userLocale = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -45,6 +49,9 @@ const darkMode = computed({
|
||||||
|
|
||||||
const user = state.getUser();
|
const user = state.getUser();
|
||||||
const token = session.getTokenMultimedia();
|
const token = session.getTokenMultimedia();
|
||||||
|
const warehousesData = ref();
|
||||||
|
const companiesData = ref();
|
||||||
|
const accountBankData = ref();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
updatePreferences();
|
updatePreferences();
|
||||||
|
@ -87,10 +94,28 @@ function copyUserToken() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Warehouses"
|
||||||
|
order="name"
|
||||||
|
@on-fetch="(data) => (warehousesData = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Companies"
|
||||||
|
order="name"
|
||||||
|
@on-fetch="(data) => (companiesData = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Accountings"
|
||||||
|
order="name"
|
||||||
|
@on-fetch="(data) => (accountBankData = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
<QMenu anchor="bottom left" class="bg-vn-section-color">
|
<QMenu anchor="bottom left" class="bg-vn-section-color">
|
||||||
<div class="row no-wrap q-pa-md">
|
<div class="row no-wrap q-pa-md">
|
||||||
<div class="column panel">
|
<div class="col column">
|
||||||
<div class="text-h6 q-mb-md">
|
<div class="text-h6 q-ma-sm q-mb-none">
|
||||||
{{ t('components.userPanel.settings') }}
|
{{ t('components.userPanel.settings') }}
|
||||||
</div>
|
</div>
|
||||||
<QToggle
|
<QToggle
|
||||||
|
@ -114,7 +139,7 @@ function copyUserToken() {
|
||||||
|
|
||||||
<QSeparator vertical inset class="q-mx-lg" />
|
<QSeparator vertical inset class="q-mx-lg" />
|
||||||
|
|
||||||
<div class="column items-center panel">
|
<div class="col column items-center q-mb-sm">
|
||||||
<QAvatar size="80px">
|
<QAvatar size="80px">
|
||||||
<QImg
|
<QImg
|
||||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||||
|
@ -131,7 +156,6 @@ function copyUserToken() {
|
||||||
>
|
>
|
||||||
@{{ user.name }}
|
@{{ user.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<QBtn
|
<QBtn
|
||||||
id="logout"
|
id="logout"
|
||||||
color="orange"
|
color="orange"
|
||||||
|
@ -141,17 +165,72 @@ function copyUserToken() {
|
||||||
icon="logout"
|
icon="logout"
|
||||||
@click="logout()"
|
@click="logout()"
|
||||||
v-close-popup
|
v-close-popup
|
||||||
|
dense
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<QSeparator inset class="q-mx-lg" />
|
||||||
|
<div class="col q-gutter-xs q-pa-md">
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.userPanel.localWarehouse')"
|
||||||
|
v-model="user.localWarehouseFk"
|
||||||
|
:options="warehousesData"
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.userPanel.localBank')"
|
||||||
|
v-model="user.localBankFk"
|
||||||
|
:options="accountBankData"
|
||||||
|
option-label="bank"
|
||||||
|
option-value="id"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ `${opt.id}: ${opt.bank}` }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelect>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.userPanel.localCompany')"
|
||||||
|
hide-selected
|
||||||
|
v-model="user.companyFk"
|
||||||
|
:options="companiesData"
|
||||||
|
option-label="code"
|
||||||
|
option-value="id"
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.userPanel.userWarehouse')"
|
||||||
|
hide-selected
|
||||||
|
v-model="user.warehouseFk"
|
||||||
|
:options="warehousesData"
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('components.userPanel.userCompany')"
|
||||||
|
hide-selected
|
||||||
|
v-model="user.companyFk"
|
||||||
|
:options="companiesData"
|
||||||
|
option-label="code"
|
||||||
|
option-value="id"
|
||||||
|
style="flex: 0"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</div>
|
||||||
</QMenu>
|
</QMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.panel {
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copyText {
|
.copyText {
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: alias;
|
cursor: alias;
|
||||||
|
|
|
@ -5,16 +5,16 @@ import { useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useCamelCase } from 'src/composables/useCamelCase';
|
import { useCamelCase } from 'src/composables/useCamelCase';
|
||||||
|
|
||||||
const router = useRouter();
|
const { currentRoute } = useRouter();
|
||||||
const quasar = useQuasar();
|
const { screen } = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t, te } = useI18n();
|
||||||
|
|
||||||
let matched = ref([]);
|
let matched = ref([]);
|
||||||
let breadcrumbs = ref([]);
|
let breadcrumbs = ref([]);
|
||||||
let root = ref(null);
|
let root = ref(null);
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
matched.value = router.currentRoute.value.matched.filter(
|
matched.value = currentRoute.value.matched.filter(
|
||||||
(matched) => Object.keys(matched.meta).length
|
(matched) => Object.keys(matched.meta).length
|
||||||
);
|
);
|
||||||
breadcrumbs.value.length = 0;
|
breadcrumbs.value.length = 0;
|
||||||
|
@ -34,13 +34,17 @@ function getBreadcrumb(param) {
|
||||||
icon: param.meta.icon,
|
icon: param.meta.icon,
|
||||||
path: param.path,
|
path: param.path,
|
||||||
root: root.value,
|
root: root.value,
|
||||||
|
locale: t(`globals.pageTitles.${param.meta.title}`),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (quasar.screen.gt.sm) {
|
if (screen.gt.sm) {
|
||||||
breadcrumb.name = param.name;
|
breadcrumb.name = param.name;
|
||||||
breadcrumb.title = useCamelCase(param.meta.title);
|
breadcrumb.title = useCamelCase(param.meta.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const moduleLocale = `${breadcrumb.root}.pageTitles.${breadcrumb.title}`;
|
||||||
|
if (te(moduleLocale)) breadcrumb.locale = t(moduleLocale);
|
||||||
|
|
||||||
return breadcrumb;
|
return breadcrumb;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -50,7 +54,7 @@ function getBreadcrumb(param) {
|
||||||
v-for="(breadcrumb, index) of breadcrumbs"
|
v-for="(breadcrumb, index) of breadcrumbs"
|
||||||
:key="index"
|
:key="index"
|
||||||
:icon="breadcrumb.icon"
|
:icon="breadcrumb.icon"
|
||||||
:label="t(`${breadcrumb.root}.pageTitles.${breadcrumb.title}`)"
|
:label="breadcrumb.locale"
|
||||||
:to="breadcrumb.path"
|
:to="breadcrumb.path"
|
||||||
/>
|
/>
|
||||||
</QBreadcrumbs>
|
</QBreadcrumbs>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<script setup>
|
||||||
|
import { onBeforeMount, computed } from 'vue';
|
||||||
|
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import useCardSize from 'src/composables/useCardSize';
|
||||||
|
import VnSubToolbar from '../ui/VnSubToolbar.vue';
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: { type: String, required: true },
|
||||||
|
baseUrl: { type: String, default: undefined },
|
||||||
|
customUrl: { type: String, default: undefined },
|
||||||
|
filter: { type: Object, default: () => {} },
|
||||||
|
descriptor: { type: Object, required: true },
|
||||||
|
searchbarDataKey: { type: String, default: undefined },
|
||||||
|
searchbarUrl: { type: String, default: undefined },
|
||||||
|
searchbarLabel: { type: String, default: '' },
|
||||||
|
searchbarInfo: { type: String, default: '' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const url = computed(() => {
|
||||||
|
if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`;
|
||||||
|
return props.customUrl;
|
||||||
|
});
|
||||||
|
|
||||||
|
const arrayData = useArrayData(props.dataKey, {
|
||||||
|
url: url.value,
|
||||||
|
filter: props.filter,
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (props.baseUrl) {
|
||||||
|
onBeforeRouteUpdate(async (to, from) => {
|
||||||
|
if (to.params.id !== from.params.id) {
|
||||||
|
arrayData.store.url = `${props.baseUrl}/${route.params.id}`;
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Teleport
|
||||||
|
to="#searchbar"
|
||||||
|
v-if="stateStore.isHeaderMounted() && props.searchbarDataKey"
|
||||||
|
>
|
||||||
|
<VnSearchbar
|
||||||
|
:data-key="props.searchbarDataKey"
|
||||||
|
:url="props.searchbarUrl"
|
||||||
|
:label="t(props.searchbarLabel)"
|
||||||
|
:info="t(props.searchbarInfo)"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
|
<QScrollArea class="fit">
|
||||||
|
<component :is="descriptor" />
|
||||||
|
<QSeparator />
|
||||||
|
<LeftMenu source="card" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPageContainer>
|
||||||
|
<QPage>
|
||||||
|
<VnSubToolbar />
|
||||||
|
<div :class="[useCardSize(), $attrs.class]">
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</QPage>
|
||||||
|
</QPageContainer>
|
||||||
|
</template>
|
|
@ -5,7 +5,7 @@ import { useCapitalize } from 'src/composables/useCapitalize';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: { type: String, default: '' },
|
modelValue: { type: [String, Number], default: '' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -6,7 +6,7 @@ import axios from 'axios';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import FormModelPopup from 'components/FormModelPopup.vue';
|
import FormModelPopup from 'components/FormModelPopup.vue';
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ function addDefaultData(data) {
|
||||||
<div class="q-gutter-y-ms">
|
<div class="q-gutter-y-ms">
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnInput :label="t('globals.reference')" v-model="dms.reference" />
|
<VnInput :label="t('globals.reference')" v-model="dms.reference" />
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('globals.company')"
|
:label="t('globals.company')"
|
||||||
v-model="dms.companyFk"
|
v-model="dms.companyFk"
|
||||||
:options="companies"
|
:options="companies"
|
||||||
|
@ -133,7 +133,7 @@ function addDefaultData(data) {
|
||||||
/>
|
/>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('globals.warehouse')"
|
:label="t('globals.warehouse')"
|
||||||
v-model="dms.warehouseFk"
|
v-model="dms.warehouseFk"
|
||||||
:options="warehouses"
|
:options="warehouses"
|
||||||
|
@ -141,7 +141,7 @@ function addDefaultData(data) {
|
||||||
option-label="name"
|
option-label="name"
|
||||||
input-debounce="0"
|
input-debounce="0"
|
||||||
/>
|
/>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('globals.type')"
|
:label="t('globals.type')"
|
||||||
v-model="dms.dmsTypeFk"
|
v-model="dms.dmsTypeFk"
|
||||||
:options="dmsTypes"
|
:options="dmsTypes"
|
||||||
|
@ -198,9 +198,13 @@ function addDefaultData(data) {
|
||||||
en:
|
en:
|
||||||
contentTypesInfo: Allowed file types {allowedContentTypes}
|
contentTypesInfo: Allowed file types {allowedContentTypes}
|
||||||
EntryDmsDescription: Reference {reference}
|
EntryDmsDescription: Reference {reference}
|
||||||
|
WorkersDescription: Working of employee id {reference}
|
||||||
|
SupplierDmsDescription: Reference {reference}
|
||||||
es:
|
es:
|
||||||
Generate identifier for original file: Generar identificador para archivo original
|
Generate identifier for original file: Generar identificador para archivo original
|
||||||
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
|
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
|
||||||
EntryDmsDescription: Referencia {reference}
|
EntryDmsDescription: Referencia {reference}
|
||||||
|
WorkersDescription: Laboral del empleado {reference}
|
||||||
|
SupplierDmsDescription: Referencia {reference}
|
||||||
|
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -5,9 +5,11 @@ import { useRoute } from 'vue-router';
|
||||||
import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
|
import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import VnDms from 'src/components/common/VnDms.vue';
|
import VnDms from 'src/components/common/VnDms.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnUserLink from '../ui/VnUserLink.vue';
|
||||||
import { downloadFile } from 'src/composables/downloadFile';
|
import { downloadFile } from 'src/composables/downloadFile';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -26,6 +28,15 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
deleteModel: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
downloadModel: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
defaultDmsCode: {
|
defaultDmsCode: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -74,7 +85,7 @@ const dmsFilter = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
order: ['dmsFk DESC'],
|
where: { [$props.filter]: route.params.id },
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
|
@ -94,12 +105,12 @@ const columns = computed(() => [
|
||||||
props: (prop) => ({
|
props: (prop) => ({
|
||||||
readonly: true,
|
readonly: true,
|
||||||
borderless: true,
|
borderless: true,
|
||||||
'model-value': prop.row.dmsType.name,
|
'model-value': prop.row.dmsType?.name,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
field: 'order',
|
field: 'hardCopyNumber',
|
||||||
label: t('globals.order'),
|
label: t('globals.order'),
|
||||||
name: 'order',
|
name: 'order',
|
||||||
component: 'span',
|
component: 'span',
|
||||||
|
@ -117,6 +128,7 @@ const columns = computed(() => [
|
||||||
label: t('globals.description'),
|
label: t('globals.description'),
|
||||||
name: 'description',
|
name: 'description',
|
||||||
component: 'span',
|
component: 'span',
|
||||||
|
props: (prop) => ({ value: prop.value?.toUpperCase() }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -136,21 +148,53 @@ const columns = computed(() => [
|
||||||
name: 'file',
|
name: 'file',
|
||||||
component: 'span',
|
component: 'span',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'worker',
|
||||||
|
label: t('globals.worker'),
|
||||||
|
name: 'worker',
|
||||||
|
component: VnUserLink,
|
||||||
|
props: (prop) => ({
|
||||||
|
name: prop.row.worker?.user?.name.toLowerCase(),
|
||||||
|
workerId: prop.row.worker?.id,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'created',
|
||||||
|
label: t('globals.created'),
|
||||||
|
name: 'created',
|
||||||
|
component: VnInputDate,
|
||||||
|
props: (prop) => ({
|
||||||
|
disable: true,
|
||||||
|
'model-value': prop.row.created,
|
||||||
|
}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'options',
|
field: 'options',
|
||||||
name: 'options',
|
name: 'options',
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
component: QBtn,
|
component: QBtn,
|
||||||
|
name: 'download',
|
||||||
|
isDocuware: true,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
icon: 'cloud_download',
|
icon: 'cloud_download',
|
||||||
flat: true,
|
flat: true,
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
}),
|
}),
|
||||||
click: (prop) => downloadFile(prop.row.id),
|
click: (prop) =>
|
||||||
|
downloadFile(
|
||||||
|
prop.row.id,
|
||||||
|
$props.downloadModel,
|
||||||
|
undefined,
|
||||||
|
prop.row.download
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: QBtn,
|
component: QBtn,
|
||||||
|
name: 'edit',
|
||||||
|
external: false,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
flat: true,
|
flat: true,
|
||||||
|
@ -160,6 +204,8 @@ const columns = computed(() => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: QBtn,
|
component: QBtn,
|
||||||
|
name: 'delete',
|
||||||
|
external: false,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
flat: true,
|
flat: true,
|
||||||
|
@ -167,12 +213,24 @@ const columns = computed(() => [
|
||||||
}),
|
}),
|
||||||
click: (prop) => deleteDms(prop.row.id),
|
click: (prop) => deleteDms(prop.row.id),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: QBtn,
|
||||||
|
name: 'open',
|
||||||
|
external: true,
|
||||||
|
props: () => ({
|
||||||
|
icon: 'open_in_new',
|
||||||
|
flat: true,
|
||||||
|
color: 'primary',
|
||||||
|
}),
|
||||||
|
click: (prop) => open(prop.row.url),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function setData(data) {
|
function setData(data) {
|
||||||
const newData = data.map((value) => value.dms);
|
const newData = data.map((value) => value.dms || value);
|
||||||
|
newData.sort((a, b) => new Date(b.created) - new Date(a.created));
|
||||||
rows.value = newData;
|
rows.value = newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +244,7 @@ function deleteDms(dmsFk) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.onOk(async () => {
|
.onOk(async () => {
|
||||||
await axios.post(`${$props.model}/${dmsFk}/removeFile`);
|
await axios.post(`${$props.deleteModel ?? $props.model}/${dmsFk}/removeFile`);
|
||||||
const index = rows.value.findIndex((row) => row.id == dmsFk);
|
const index = rows.value.findIndex((row) => row.id == dmsFk);
|
||||||
rows.value.splice(index, 1);
|
rows.value.splice(index, 1);
|
||||||
});
|
});
|
||||||
|
@ -206,19 +264,29 @@ function parseDms(data) {
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function open(url) {
|
||||||
|
window.open(url).focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldRenderButton(button, isExternal = false) {
|
||||||
|
if (button.name == 'download') return true;
|
||||||
|
return button.external === isExternal;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<VnPaginate
|
||||||
ref="dmsRef"
|
ref="dmsRef"
|
||||||
|
:data-key="$props.model"
|
||||||
:url="$props.model"
|
:url="$props.model"
|
||||||
:filter="dmsFilter"
|
:filter="dmsFilter"
|
||||||
:where="{ [$props.filter]: route.params.id }"
|
:order="['dmsFk DESC']"
|
||||||
|
:auto-load="true"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
auto-load
|
>
|
||||||
/>
|
<template #body>
|
||||||
<QTable
|
<QTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
hide-bottom
|
hide-bottom
|
||||||
|
@ -241,9 +309,12 @@ function parseDms(data) {
|
||||||
</component>
|
</component>
|
||||||
</QTr>
|
</QTr>
|
||||||
|
|
||||||
<div class="flex justify-center" v-if="props.col.name == 'options'">
|
<div class="row no-wrap" v-if="props.col.name == 'options'">
|
||||||
<div v-for="button of props.col.components" :key="button.id">
|
<div v-for="button of props.col.components" :key="button.id">
|
||||||
<component
|
<component
|
||||||
|
v-if="
|
||||||
|
shouldRenderButton(button, props.row.isDocuware)
|
||||||
|
"
|
||||||
:is="button.component"
|
:is="button.component"
|
||||||
v-bind="button.props(props)"
|
v-bind="button.props(props)"
|
||||||
@click="button.click(props)"
|
@click="button.click(props)"
|
||||||
|
@ -273,6 +344,12 @@ function parseDms(data) {
|
||||||
class="row"
|
class="row"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
|
v-if="
|
||||||
|
shouldRenderButton(
|
||||||
|
button.name,
|
||||||
|
props.row.isDocuware
|
||||||
|
)
|
||||||
|
"
|
||||||
:is="button.component"
|
:is="button.component"
|
||||||
v-bind="button.props(col)"
|
v-bind="button.props(col)"
|
||||||
@click="button.click(col)"
|
@click="button.click(col)"
|
||||||
|
@ -285,6 +362,8 @@ function parseDms(data) {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
<QDialog v-model="formDialog.show">
|
<QDialog v-model="formDialog.show">
|
||||||
<VnDms
|
<VnDms
|
||||||
:model="updateModel ?? model"
|
:model="updateModel ?? model"
|
||||||
|
|
|
@ -26,7 +26,7 @@ const value = computed({
|
||||||
emit('update:modelValue', value);
|
emit('update:modelValue', value);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const hover = ref(false);
|
||||||
const styleAttrs = computed(() => {
|
const styleAttrs = computed(() => {
|
||||||
return $props.isOutlined
|
return $props.isOutlined
|
||||||
? {
|
? {
|
||||||
|
@ -41,6 +41,10 @@ const onEnterPress = () => {
|
||||||
emit('keyup.enter');
|
emit('keyup.enter');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleValue = (val = null) => {
|
||||||
|
value.value = val;
|
||||||
|
};
|
||||||
|
|
||||||
const focus = () => {
|
const focus = () => {
|
||||||
vnInputRef.value.focus();
|
vnInputRef.value.focus();
|
||||||
};
|
};
|
||||||
|
@ -51,20 +55,33 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div
|
||||||
|
@mouseover="hover = true"
|
||||||
|
@mouseleave="hover = false"
|
||||||
|
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||||
|
>
|
||||||
<QInput
|
<QInput
|
||||||
ref="vnInputRef"
|
ref="vnInputRef"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
type="text"
|
:type="$attrs.type"
|
||||||
:class="{ required: $attrs.required }"
|
:class="{ required: $attrs.required }"
|
||||||
@keyup.enter="onEnterPress()"
|
@keyup.enter="onEnterPress()"
|
||||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
:clearable="false"
|
||||||
>
|
>
|
||||||
<template v-if="$slots.prepend" #prepend>
|
<template v-if="$slots.prepend" #prepend>
|
||||||
<slot name="prepend" />
|
<slot name="prepend" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$slots.append" #append>
|
|
||||||
<slot name="append" />
|
<template #append>
|
||||||
|
<slot name="append" v-if="$slots.append" />
|
||||||
|
<QIcon
|
||||||
|
name="close"
|
||||||
|
size="xs"
|
||||||
|
v-if="hover && value"
|
||||||
|
@click="handleValue(null)"
|
||||||
|
></QIcon>
|
||||||
</template>
|
</template>
|
||||||
</QInput>
|
</QInput>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
import isValidDate from 'filters/isValidDate';
|
||||||
import isValidDate from "filters/isValidDate";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -16,7 +15,13 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
emitDateFormat: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
const hover = ref(false);
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const joinDateAndTime = (date, time) => {
|
const joinDateAndTime = (date, time) => {
|
||||||
|
@ -36,7 +41,10 @@ const value = computed({
|
||||||
return props.modelValue;
|
return props.modelValue;
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
emit('update:modelValue', joinDateAndTime(value, time.value));
|
emit(
|
||||||
|
'update:modelValue',
|
||||||
|
props.emitDateFormat ? new Date(value) : joinDateAndTime(value, time.value)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,14 +85,21 @@ const styleAttrs = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnInput
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||||
|
<QInput
|
||||||
class="vn-input-date"
|
class="vn-input-date"
|
||||||
|
readonly
|
||||||
:model-value="displayDate(value)"
|
:model-value="displayDate(value)"
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
readonly
|
|
||||||
@click="isPopupOpen = true"
|
@click="isPopupOpen = true"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
|
<QIcon
|
||||||
|
name="close"
|
||||||
|
size="xs"
|
||||||
|
v-if="hover && value"
|
||||||
|
@click="onDateUpdate(null)"
|
||||||
|
></QIcon>
|
||||||
<QIcon name="event" class="cursor-pointer">
|
<QIcon name="event" class="cursor-pointer">
|
||||||
<QPopupProxy
|
<QPopupProxy
|
||||||
v-model="isPopupOpen"
|
v-model="isPopupOpen"
|
||||||
|
@ -94,13 +109,15 @@ const styleAttrs = computed(() => {
|
||||||
:no-parent-event="props.readonly"
|
:no-parent-event="props.readonly"
|
||||||
>
|
>
|
||||||
<QDate
|
<QDate
|
||||||
|
:today-btn="true"
|
||||||
:model-value="formatDate(value)"
|
:model-value="formatDate(value)"
|
||||||
@update:model-value="onDateUpdate"
|
@update:model-value="onDateUpdate"
|
||||||
/>
|
/>
|
||||||
</QPopupProxy>
|
</QPopupProxy>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</template>
|
</template>
|
||||||
</VnInput>
|
</QInput>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import isValidDate from 'filters/isValidDate';
|
import isValidDate from 'filters/isValidDate';
|
||||||
import VnInput from "components/common/VnInput.vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -20,6 +19,7 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.modelValue;
|
return props.modelValue;
|
||||||
|
@ -27,12 +27,7 @@ const value = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
const [hours, minutes] = value.split(':');
|
const [hours, minutes] = value.split(':');
|
||||||
const date = new Date(props.modelValue);
|
const date = new Date(props.modelValue);
|
||||||
date.setHours(
|
date.setHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0);
|
||||||
Number.parseInt(hours) || 0,
|
|
||||||
Number.parseInt(minutes) || 0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
emit('update:modelValue', value ? date.toISOString() : null);
|
emit('update:modelValue', value ? date.toISOString() : null);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -71,7 +66,7 @@ const styleAttrs = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnInput
|
<QInput
|
||||||
class="vn-input-time"
|
class="vn-input-time"
|
||||||
readonly
|
readonly
|
||||||
:model-value="formatTime(value)"
|
:model-value="formatTime(value)"
|
||||||
|
@ -79,7 +74,7 @@ const styleAttrs = computed(() => {
|
||||||
@click="isPopupOpen = true"
|
@click="isPopupOpen = true"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QIcon name="event" class="cursor-pointer">
|
<QIcon name="schedule" class="cursor-pointer">
|
||||||
<QPopupProxy
|
<QPopupProxy
|
||||||
v-model="isPopupOpen"
|
v-model="isPopupOpen"
|
||||||
cover
|
cover
|
||||||
|
@ -111,7 +106,7 @@ const styleAttrs = computed(() => {
|
||||||
</QPopupProxy>
|
</QPopupProxy>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</template>
|
</template>
|
||||||
</VnInput>
|
</QInput>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -88,6 +88,10 @@ function locationFilter(search = '') {
|
||||||
function handleFetch(data) {
|
function handleFetch(data) {
|
||||||
postcodesOptions.value = data;
|
postcodesOptions.value = data;
|
||||||
}
|
}
|
||||||
|
function onDataSaved(newPostcode) {
|
||||||
|
postcodesOptions.value.push(newPostcode);
|
||||||
|
value.value = newPostcode.code;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
@ -111,11 +115,13 @@ function handleFetch(data) {
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewPostcode @on-data-saved="locationFilter()" />
|
<CreateNewPostcode
|
||||||
|
@on-data-saved="onDataSaved"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #option="{ itemProps, opt }">
|
<template #option="{ itemProps, opt }">
|
||||||
<QItem v-bind="itemProps">
|
<QItem v-bind="itemProps">
|
||||||
<QItemSection v-if="opt">
|
<QItemSection v-if="opt.code">
|
||||||
<QItemLabel>{{ opt.code }}</QItemLabel>
|
<QItemLabel>{{ opt.code }}</QItemLabel>
|
||||||
<QItemLabel caption>{{ showLabel(opt) }}</QItemLabel>
|
<QItemLabel caption>{{ showLabel(opt) }}</QItemLabel>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { useValidator } from 'src/composables/useValidator';
|
||||||
import VnAvatar from '../ui/VnAvatar.vue';
|
import VnAvatar from '../ui/VnAvatar.vue';
|
||||||
import VnJsonValue from '../common/VnJsonValue.vue';
|
import VnJsonValue from '../common/VnJsonValue.vue';
|
||||||
import FetchData from '../FetchData.vue';
|
import FetchData from '../FetchData.vue';
|
||||||
import VnSelectFilter from './VnSelectFilter.vue';
|
import VnSelect from './VnSelect.vue';
|
||||||
import VnUserLink from '../ui/VnUserLink.vue';
|
import VnUserLink from '../ui/VnUserLink.vue';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
@ -403,7 +403,7 @@ setLogTree();
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="column items-center logs origin-log"
|
class="column items-center logs origin-log q-mt-md"
|
||||||
v-for="(originLog, originLogIndex) in logTree"
|
v-for="(originLog, originLogIndex) in logTree"
|
||||||
:key="originLogIndex"
|
:key="originLogIndex"
|
||||||
>
|
>
|
||||||
|
@ -421,12 +421,13 @@ setLogTree();
|
||||||
>
|
>
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<div class="user-avatar">
|
<div class="user-avatar">
|
||||||
<VnUserLink :worker-id="userLog.user.id">
|
<VnUserLink :worker-id="userLog?.user?.id">
|
||||||
<template #link>
|
<template #link>
|
||||||
<VnAvatar
|
<VnAvatar
|
||||||
:class="{ 'cursor-pointer': userLog.user.id }"
|
:class="{ 'cursor-pointer': userLog?.user?.id }"
|
||||||
:worker-id="userLog.user.id"
|
:worker-id="userLog?.user?.id"
|
||||||
:title="userLog.user.nickname"
|
:title="userLog?.user?.nickname"
|
||||||
|
:show-letter="!userLog?.user"
|
||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -659,7 +660,7 @@ setLogTree();
|
||||||
</QInput>
|
</QInput>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem>
|
<QItem>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:label="t('globals.entity')"
|
:label="t('globals.entity')"
|
||||||
v-model="selectedFilters.changedModel"
|
v-model="selectedFilters.changedModel"
|
||||||
|
@ -689,7 +690,7 @@ setLogTree();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers && userRadio !== null">
|
<QItemSection v-if="workers && userRadio !== null">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:label="t('globals.user')"
|
:label="t('globals.user')"
|
||||||
v-model="userSelect"
|
v-model="userSelect"
|
||||||
|
@ -713,7 +714,7 @@ setLogTree();
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelect>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem class="q-mt-sm">
|
<QItem class="q-mt-sm">
|
||||||
|
@ -1030,7 +1031,7 @@ en:
|
||||||
ticketCreated: Created
|
ticketCreated: Created
|
||||||
created: Created
|
created: Created
|
||||||
isChargedToMana: Charged to mana
|
isChargedToMana: Charged to mana
|
||||||
hasToPickUp: Has to pick Up
|
pickup: Type of pickup
|
||||||
dmsFk: Document ID
|
dmsFk: Document ID
|
||||||
text: Description
|
text: Description
|
||||||
claimStateFk: Claim State
|
claimStateFk: Claim State
|
||||||
|
@ -1069,7 +1070,7 @@ es:
|
||||||
ticketCreated: Creado
|
ticketCreated: Creado
|
||||||
created: Creado
|
created: Creado
|
||||||
isChargedToMana: Cargado a maná
|
isChargedToMana: Cargado a maná
|
||||||
hasToPickUp: Se debe recoger
|
pickup: Se debe recoger
|
||||||
dmsFk: ID documento
|
dmsFk: ID documento
|
||||||
text: Descripción
|
text: Descripción
|
||||||
claimStateFk: Estado de la reclamación
|
claimStateFk: Estado de la reclamación
|
||||||
|
|
|
@ -51,8 +51,8 @@ const $props = defineProps({
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: [Number, String],
|
||||||
default: 30,
|
default: '30',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ watch(modelValue, (newValue) => {
|
||||||
@on-fetch="(data) => setOptions(data)"
|
@on-fetch="(data) => setOptions(data)"
|
||||||
:where="where || { [optionValue]: value }"
|
:where="where || { [optionValue]: value }"
|
||||||
:limit="limit"
|
:limit="limit"
|
||||||
:order-by="orderBy"
|
:sort-by="sortBy"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
/>
|
/>
|
||||||
<QSelect
|
<QSelect
|
||||||
|
@ -169,6 +169,7 @@ watch(modelValue, (newValue) => {
|
||||||
ref="vnSelectRef"
|
ref="vnSelectRef"
|
||||||
:class="{ required: $attrs.required }"
|
:class="{ required: $attrs.required }"
|
||||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||||
|
virtual-scroll-slice-size="options.length"
|
||||||
>
|
>
|
||||||
<template v-if="isClearable" #append>
|
<template v-if="isClearable" #append>
|
||||||
<QIcon
|
<QIcon
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
|
||||||
import { useRole } from 'src/composables/useRole';
|
import { useRole } from 'src/composables/useRole';
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ const toggleForm = () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VnSelectFilter v-model="value" :options="options" v-bind="$attrs">
|
<VnSelect v-model="value" :options="options" v-bind="$attrs">
|
||||||
<template v-if="isAllowedToCreate" #append>
|
<template v-if="isAllowedToCreate" #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
@click.stop.prevent="toggleForm()"
|
@click.stop.prevent="toggleForm()"
|
||||||
|
@ -72,7 +72,7 @@ const toggleForm = () => {
|
||||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
||||||
<slot :name="slotName" v-bind="slotData" :key="slotName" />
|
<slot :name="slotName" v-bind="slotData" :key="slotName" />
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -21,7 +21,8 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
template: {
|
template: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: false,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
locale: {
|
locale: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -49,7 +50,7 @@ updateMessage();
|
||||||
|
|
||||||
function updateMessage() {
|
function updateMessage() {
|
||||||
const params = props.data;
|
const params = props.data;
|
||||||
const key = `templates['${props.template}']`;
|
const key = props.template ? `templates['${props.template}']` : '';
|
||||||
|
|
||||||
message.value = t(key, params, { locale: locale.value });
|
message.value = t(key, params, { locale: locale.value });
|
||||||
}
|
}
|
||||||
|
@ -104,15 +105,14 @@ async function send() {
|
||||||
map-options
|
map-options
|
||||||
:input-debounce="0"
|
:input-debounce="0"
|
||||||
rounded
|
rounded
|
||||||
outlined
|
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pb-xs">
|
<QCardSection class="q-pb-xs">
|
||||||
<VnInput :label="t('Phone')" v-model="phone" is-outlined />
|
<VnInput :label="t('Phone')" v-model="phone" />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pb-xs">
|
<QCardSection class="q-pb-xs">
|
||||||
<VnInput v-model="subject" :label="t('Subject')" is-outlined />
|
<VnInput v-model="subject" :label="t('Subject')" />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-mb-md" q-input>
|
<QCardSection class="q-mb-md" q-input>
|
||||||
<QInput
|
<QInput
|
||||||
|
@ -125,7 +125,6 @@ async function send() {
|
||||||
:bottom-slots="true"
|
:bottom-slots="true"
|
||||||
:rules="[(value) => value.length < maxLength || 'Error!']"
|
:rules="[(value) => value.length < maxLength || 'Error!']"
|
||||||
stack-label
|
stack-label
|
||||||
outlined
|
|
||||||
autofocus
|
autofocus
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
|
@ -135,6 +134,11 @@ async function send() {
|
||||||
@click="message = ''"
|
@click="message = ''"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
/>
|
/>
|
||||||
|
<QIcon name="info" class="cursor-pointer">
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('messageTooltip') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
</template>
|
</template>
|
||||||
<template #counter>
|
<template #counter>
|
||||||
<QChip :color="color" dense>
|
<QChip :color="color" dense>
|
||||||
|
@ -184,18 +188,21 @@ en:
|
||||||
es: Spanish
|
es: Spanish
|
||||||
fr: French
|
fr: French
|
||||||
pt: Portuguese
|
pt: Portuguese
|
||||||
|
messageTooltip: Special characters like accents counts as multiple
|
||||||
es:
|
es:
|
||||||
Send SMS: Enviar SMS
|
Send SMS: Enviar SMS
|
||||||
Language: Idioma
|
Language: Idioma
|
||||||
Phone: Móvil
|
Phone: Móvil
|
||||||
Subject: Asunto
|
Subject: Asunto
|
||||||
Message: Mensaje
|
Message: Mensaje
|
||||||
|
messageTooltip: Carácteres especiales como acentos cuentan como varios
|
||||||
templates:
|
templates:
|
||||||
pendingPayment: 'Su pedido está pendiente de pago.
|
pendingPayment: 'Su pedido está pendiente de pago.
|
||||||
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
|
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
|
||||||
minAmount: 'Es necesario un importe mínimo de 50€ (Sin IVA) en su pedido
|
minAmount: 'Te recordamos que tu pedido {orderId} es inferior a 50€.
|
||||||
{ orderId } del día { shipped } para recibirlo sin portes adicionales.'
|
Te recomendamos amplíes para no generar costes extra, provocarán un incremento de tu tarifa.
|
||||||
orderChanges: 'Pedido {orderId} día { shipped }: { changes }'
|
¡Un saludo!'
|
||||||
|
orderChanges: 'Pedido {orderId} con llegada estimada día { landing }: { changes }'
|
||||||
en: Inglés
|
en: Inglés
|
||||||
es: Español
|
es: Español
|
||||||
fr: Francés
|
fr: Francés
|
||||||
|
@ -207,12 +214,14 @@ fr:
|
||||||
Phone: Mobile
|
Phone: Mobile
|
||||||
Subject: Affaire
|
Subject: Affaire
|
||||||
Message: Message
|
Message: Message
|
||||||
|
messageTooltip: Les caractères spéciaux comme les accents comptent comme plusieurs
|
||||||
templates:
|
templates:
|
||||||
pendingPayment: 'Votre commande est en attente de paiement.
|
pendingPayment: 'Verdnatura : Commande en attente de règlement. Veuillez régler votre commande avant 9h.
|
||||||
Veuillez vous connecter sur le site web et effectuer le paiement par carte. Merci beaucoup.'
|
Sinon elle sera décalée en fonction de vos jours de livraison . Merci'
|
||||||
minAmount: 'Un montant minimum de 50€ (TVA non incluse) est requis pour votre commande
|
minAmount: 'Verdnatura vous rappelle :
|
||||||
{ orderId } du { shipped } afin de la recevoir sans frais de port supplémentaires.'
|
Montant minimum nécessaire de 50 euros pour recevoir la commande { orderId } livraison { landing }.
|
||||||
orderChanges: 'Commande { orderId } du { shipped }: { changes }'
|
Merci.'
|
||||||
|
orderChanges: 'Commande {orderId} livraison {landing} indisponible/s. Désolés pour le dérangement.'
|
||||||
en: Anglais
|
en: Anglais
|
||||||
es: Espagnol
|
es: Espagnol
|
||||||
fr: Français
|
fr: Français
|
||||||
|
@ -224,12 +233,13 @@ pt:
|
||||||
Phone: Móvel
|
Phone: Móvel
|
||||||
Subject: Assunto
|
Subject: Assunto
|
||||||
Message: Mensagem
|
Message: Mensagem
|
||||||
|
messageTooltip: Caracteres especiais como acentos contam como vários
|
||||||
templates:
|
templates:
|
||||||
pendingPayment: 'Seu pedido está pendente de pagamento.
|
pendingPayment: 'Seu pedido está pendente de pagamento.
|
||||||
Por favor, acesse o site e faça o pagamento com cartão. Muito obrigado.'
|
Por favor, acesse o site e faça o pagamento com cartão. Muito obrigado.'
|
||||||
minAmount: 'É necessário um valor mínimo de 50€ (sem IVA) em seu pedido
|
minAmount: 'É necessário um valor mínimo de 50€ (sem IVA) em seu pedido
|
||||||
{ orderId } do dia { shipped } para recebê-lo sem custos de envio adicionais.'
|
{ orderId } do dia { landing } para recebê-lo sem custos de envio adicionais.'
|
||||||
orderChanges: 'Pedido { orderId } dia { shipped }: { changes }'
|
orderChanges: 'Pedido { orderId } com chegada dia { landing }: { changes }'
|
||||||
en: Inglês
|
en: Inglês
|
||||||
es: Espanhol
|
es: Espanhol
|
||||||
fr: Francês
|
fr: Francês
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, useSlots, watch, computed, ref } from 'vue';
|
import { onBeforeMount, watch, computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
@ -38,37 +38,33 @@ const $props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const slots = useSlots();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
const entity = computed(() => useArrayData($props.dataKey).store.data);
|
const arrayData = useArrayData($props.dataKey || $props.module, {
|
||||||
|
url: $props.url,
|
||||||
|
filter: $props.filter,
|
||||||
|
skip: 0,
|
||||||
|
});
|
||||||
|
const { store } = arrayData;
|
||||||
|
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
getData,
|
getData,
|
||||||
});
|
});
|
||||||
onMounted(async () => {
|
onBeforeMount(async () => {
|
||||||
await getData();
|
await getData();
|
||||||
watch(
|
watch($props, async () => await getData());
|
||||||
() => $props.url,
|
|
||||||
async (newUrl, lastUrl) => {
|
|
||||||
if (newUrl == lastUrl) return;
|
|
||||||
await getData();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getData() {
|
async function getData() {
|
||||||
const arrayData = useArrayData($props.dataKey, {
|
store.url = $props.url;
|
||||||
url: $props.url,
|
store.filter = $props.filter ?? {};
|
||||||
filter: $props.filter,
|
|
||||||
skip: 0,
|
|
||||||
});
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
|
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
|
||||||
state.set($props.dataKey, data);
|
state.set($props.dataKey, data);
|
||||||
emit('onFetch', data);
|
emit('onFetch', Array.isArray(data) ? data[0] : data);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +114,7 @@ const emit = defineEmits(['onFetch']);
|
||||||
icon="more_vert"
|
icon="more_vert"
|
||||||
round
|
round
|
||||||
size="md"
|
size="md"
|
||||||
:class="{ invisible: !slots.menu }"
|
:class="{ invisible: !$slots.menu }"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('components.cardDescriptor.moreOptions') }}
|
{{ t('components.cardDescriptor.moreOptions') }}
|
||||||
|
@ -158,7 +154,7 @@ const emit = defineEmits(['onFetch']);
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<slot name="icons" :entity="entity" />
|
<slot name="icons" :entity="entity" />
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions justify-center">
|
||||||
<slot name="actions" :entity="entity" />
|
<slot name="actions" :entity="entity" />
|
||||||
</div>
|
</div>
|
||||||
<slot name="after" />
|
<slot name="after" />
|
||||||
|
@ -177,22 +173,23 @@ const emit = defineEmits(['onFetch']);
|
||||||
.body {
|
.body {
|
||||||
background-color: var(--vn-section-color);
|
background-color: var(--vn-section-color);
|
||||||
.text-h5 {
|
.text-h5 {
|
||||||
|
font-size: 20px;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
.q-item {
|
.q-item {
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
margin-left: 5px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.vn-label-value {
|
.vn-label-value {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 2px 16px;
|
padding: 0px 16px;
|
||||||
.label {
|
.label {
|
||||||
color: var(--vn-label-color);
|
color: var(--vn-label-color);
|
||||||
font-size: 12px;
|
font-size: 14px;
|
||||||
|
|
||||||
&:not(:has(a))::after {
|
&:not(:has(a))::after {
|
||||||
content: ':';
|
content: ':';
|
||||||
|
@ -201,7 +198,7 @@ const emit = defineEmits(['onFetch']);
|
||||||
.value {
|
.value {
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-left: 12px;
|
margin-left: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -219,18 +216,19 @@ const emit = defineEmits(['onFetch']);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
span {
|
span {
|
||||||
color: $primary;
|
color: var(--vn-text-color);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
.list-box {
|
.list-box {
|
||||||
.q-item__label {
|
.q-item__label {
|
||||||
color: var(--vn-label-color);
|
color: var(--vn-label-color);
|
||||||
|
padding-bottom: 0%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.descriptor {
|
.descriptor {
|
||||||
|
@ -248,6 +246,7 @@ const emit = defineEmits(['onFetch']);
|
||||||
}
|
}
|
||||||
.actions {
|
.actions {
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
|
justify-content: center !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { ref, computed, watch, onBeforeMount } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
|
||||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
|
||||||
const entity = ref();
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -16,46 +15,52 @@ const props = defineProps({
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
entityId: {
|
entityId: {
|
||||||
type: Number,
|
type: [Number, String],
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['onFetch']);
|
const emit = defineEmits(['onFetch']);
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const isSummary = ref();
|
const isSummary = ref();
|
||||||
|
const arrayData = useArrayData(props.dataKey || route.meta.moduleName, {
|
||||||
|
url: props.url,
|
||||||
|
filter: props.filter,
|
||||||
|
skip: 0,
|
||||||
|
});
|
||||||
|
const { store } = arrayData;
|
||||||
|
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
entity,
|
entity,
|
||||||
fetch,
|
fetch,
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onBeforeMount(async () => {
|
||||||
isSummary.value = String(route.path).endsWith('/summary');
|
isSummary.value = String(route.path).endsWith('/summary');
|
||||||
fetch();
|
await fetch();
|
||||||
|
watch(props, async () => await fetch());
|
||||||
});
|
});
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const params = {};
|
store.url = props.url;
|
||||||
|
store.filter = props.filter ?? {};
|
||||||
if (props.filter) params.filter = JSON.stringify(props.filter);
|
isLoading.value = true;
|
||||||
|
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
|
||||||
const { data } = await axios.get(props.url, { params });
|
emit('onFetch', Array.isArray(data) ? data[0] : data);
|
||||||
entity.value = data;
|
isLoading.value = false;
|
||||||
|
|
||||||
emit('onFetch', data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(props, async () => {
|
|
||||||
entity.value = null;
|
|
||||||
fetch();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="summary container">
|
<div class="summary container">
|
||||||
<QCard class="cardSummary">
|
<QCard class="cardSummary">
|
||||||
<SkeletonSummary v-if="!entity" />
|
<SkeletonSummary v-if="!entity || isLoading" />
|
||||||
<template v-if="entity">
|
<template v-if="entity && !isLoading">
|
||||||
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
||||||
<slot name="header-left">
|
<slot name="header-left">
|
||||||
<router-link
|
<router-link
|
||||||
|
@ -70,7 +75,7 @@ watch(props, async () => {
|
||||||
</router-link>
|
</router-link>
|
||||||
<span v-else></span>
|
<span v-else></span>
|
||||||
</slot>
|
</slot>
|
||||||
<slot name="header" :entity="entity">
|
<slot name="header" :entity="entity" dense>
|
||||||
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
||||||
</slot>
|
</slot>
|
||||||
<slot name="header-right">
|
<slot name="header-right">
|
||||||
|
@ -93,7 +98,6 @@ watch(props, async () => {
|
||||||
|
|
||||||
.cardSummary {
|
.cardSummary {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.summaryHeader {
|
.summaryHeader {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
@ -128,6 +132,7 @@ watch(props, async () => {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
min-width: 275px;
|
min-width: 275px;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
.vn-label-value {
|
.vn-label-value {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import '@quasar/quasar-ui-qcalendar/src/QCalendarVariables.sass';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
bordered: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
transparentBackground: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
viewCustomization: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const $q = useQuasar();
|
||||||
|
|
||||||
|
// El objetivo de asignar las clases de personalización desde el wrapper es no tener conflictos entre vistas que usen el mismo componente
|
||||||
|
const viewCustomizationClasses = {
|
||||||
|
workerCalendar: 'worker-calendar-customizations',
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerClasses = computed(() => {
|
||||||
|
const classes = ['main-container-background'];
|
||||||
|
if (viewCustomizationClasses[$props.viewCustomization])
|
||||||
|
classes.push(viewCustomizationClasses[$props.viewCustomization]);
|
||||||
|
if ($props.bordered) classes.push('--bordered');
|
||||||
|
if ($props.transparentBackground) classes.push('transparent-background');
|
||||||
|
else classes.push($q.dark.isActive ? '--dark' : '--light');
|
||||||
|
return classes;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="containerClasses">
|
||||||
|
<div class="nav-container row"><slot name="header" /></div>
|
||||||
|
<slot name="calendar" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../css/quasar.variables.scss';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
// Cambia los colores del día actual del calendario por los de salix
|
||||||
|
--calendar-border-current-dark: #84d0e2 2px solid;
|
||||||
|
--calendar-border-current: #84d0e2 2px solid;
|
||||||
|
--calendar-current-color-dark: #84d0e2;
|
||||||
|
// Colores de fondo del calendario en dark mode
|
||||||
|
--calendar-outside-background-dark: #222;
|
||||||
|
--calendar-background-dark: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clases para modificar el color de fecha seleccionada en componente QCalendarMonth
|
||||||
|
.q-dark div .q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button {
|
||||||
|
background-color: $primary !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button {
|
||||||
|
background-color: $primary !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-month__head--weekday {
|
||||||
|
// Transforma los nombres de los días de la semana a mayúsculas
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transparent-background {
|
||||||
|
--calendar-background-dark: transparent;
|
||||||
|
--calendar-background: transparent;
|
||||||
|
--calendar-outside-background-dark: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar__button {
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--vn-accent-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container-background {
|
||||||
|
--calendar-current-background-dark: transparent;
|
||||||
|
|
||||||
|
&.--dark {
|
||||||
|
background-color: var(--calendar-background-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.--light {
|
||||||
|
background-color: var(--calendar-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.--bordered {
|
||||||
|
border: 1px solid #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-calendar-customizations {
|
||||||
|
.q-calendar__button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--vn-accent-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-month__week--days > div:nth-child(6),
|
||||||
|
.q-calendar-month__week--days > div:nth-child(7) {
|
||||||
|
// Cambia el color de los días sábado y domingo
|
||||||
|
color: #777777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-month__week--wrapper {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-month__workweek {
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar__button--bordered {
|
||||||
|
color: $info !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-month__day--content {
|
||||||
|
position: absolute;
|
||||||
|
top: 1;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-outside .calendar-event {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-calendar-month__workweek,
|
||||||
|
.q-calendar-month__head--workweek,
|
||||||
|
.q-calendar-month__head--weekday.q-calendar__center.q-calendar__ellipsis {
|
||||||
|
text-transform: capitalize;
|
||||||
|
color: #777;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.--bordered {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -79,6 +79,7 @@ watch(
|
||||||
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
async function search() {
|
async function search() {
|
||||||
|
store.filter.where = {};
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
const params = { ...userParams.value };
|
const params = { ...userParams.value };
|
||||||
store.userParamsChanged = true;
|
store.userParamsChanged = true;
|
||||||
|
@ -164,7 +165,7 @@ function formatValue(value) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QForm @submit="search">
|
<QForm @submit="search" id="filterPanelForm">
|
||||||
<QList dense>
|
<QList dense>
|
||||||
<QItem class="q-mt-xs">
|
<QItem class="q-mt-xs">
|
||||||
<QItemSection top>
|
<QItemSection top>
|
||||||
|
|
|
@ -15,7 +15,6 @@ const { t } = useI18n();
|
||||||
color="primary"
|
color="primary"
|
||||||
padding="none"
|
padding="none"
|
||||||
:href="`sip:${props.phoneNumber}`"
|
:href="`sip:${props.phoneNumber}`"
|
||||||
:title="t('globals.microsip')"
|
|
||||||
@click.stop
|
@click.stop
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import VnAvatar from 'src/components/ui/VnAvatar.vue';
|
import VnAvatar from 'src/components/ui/VnAvatar.vue';
|
||||||
import { toDateHour } from 'src/filters';
|
import { toDateHourMin } from 'src/filters';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import VnPaginate from './VnPaginate.vue';
|
import VnPaginate from './VnPaginate.vue';
|
||||||
import VnUserLink from '../ui/VnUserLink.vue';
|
import VnUserLink from '../ui/VnUserLink.vue';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
url: { type: String, default: null },
|
url: { type: String, default: null },
|
||||||
|
@ -13,8 +14,10 @@ const $props = defineProps({
|
||||||
body: { type: Object, default: () => {} },
|
body: { type: Object, default: () => {} },
|
||||||
addNote: { type: Boolean, default: false },
|
addNote: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const noteModal = ref(false);
|
const state = useState();
|
||||||
|
const currentUser = ref(state.getUser());
|
||||||
const newNote = ref('');
|
const newNote = ref('');
|
||||||
const vnPaginateRef = ref();
|
const vnPaginateRef = ref();
|
||||||
|
|
||||||
|
@ -22,98 +25,83 @@ async function insert() {
|
||||||
const body = $props.body;
|
const body = $props.body;
|
||||||
Object.assign(body, { text: newNote.value });
|
Object.assign(body, { text: newNote.value });
|
||||||
await axios.post($props.url, body);
|
await axios.post($props.url, body);
|
||||||
vnPaginateRef.value.fetch();
|
await vnPaginateRef.value.fetch();
|
||||||
newNote.value = '';
|
newNote.value = '';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column items-center full-height full-width">
|
<QCard class="q-pa-xs q-mb-xl full-width" v-if="$props.addNote">
|
||||||
<VnPaginate
|
|
||||||
:data-key="$props.url"
|
|
||||||
:url="$props.url"
|
|
||||||
order="created DESC"
|
|
||||||
:limit="20"
|
|
||||||
:filter="$props.filter"
|
|
||||||
auto-load
|
|
||||||
ref="vnPaginateRef"
|
|
||||||
>
|
|
||||||
<template #body="{ rows }">
|
|
||||||
<div class="column items-center full-width">
|
|
||||||
<QCard
|
|
||||||
class="q-pa-xs q-mb-sm full-width"
|
|
||||||
v-for="(note, index) in rows"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<QCardSection horizontal>
|
<QCardSection horizontal>
|
||||||
<slot name="picture">
|
<VnAvatar :worker-id="currentUser.id" size="md" />
|
||||||
<VnAvatar
|
|
||||||
:descriptor="false"
|
|
||||||
:worker-id="note.workerFk"
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
<div class="full-width row justify-between q-pa-xs">
|
<div class="full-width row justify-between q-pa-xs">
|
||||||
<VnUserLink
|
<VnUserLink :name="t('New note')" :worker-id="currentUser.id" />
|
||||||
:name="`${note.worker.user.nickname}`"
|
{{ t('globals.now') }}
|
||||||
:worker-id="note.worker.id"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<slot name="actions">
|
|
||||||
{{ toDateHour(note.created) }}
|
|
||||||
</slot>
|
|
||||||
</div>
|
</div>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-pa-xs q-my-none q-py-none">
|
<QCardSection class="q-pa-xs q-my-none q-py-none" horizontal>
|
||||||
<slot name="text">
|
|
||||||
{{ note.text }}
|
|
||||||
</slot>
|
|
||||||
</QCardSection>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VnPaginate>
|
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]" v-if="addNote">
|
|
||||||
<QBtn color="primary" icon="add" size="lg" round @click="noteModal = true" />
|
|
||||||
</QPageSticky>
|
|
||||||
<QDialog v-model="noteModal" @hide="newNote = ''">
|
|
||||||
<QCard>
|
|
||||||
<QCardSection>
|
|
||||||
<QItem class="q-px-none">
|
|
||||||
<span class="text-primary text-h6 full-width">
|
|
||||||
<QIcon name="draft" class="q-mr-xs" />
|
|
||||||
{{ t('Add note') }}
|
|
||||||
</span>
|
|
||||||
<QBtn icon="close" flat round dense v-close-popup />
|
|
||||||
</QItem>
|
|
||||||
</QCardSection>
|
|
||||||
<QCardSection>
|
|
||||||
<QInput
|
<QInput
|
||||||
autofocus
|
v-model="newNote"
|
||||||
|
class="full-width"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:label="t('Add note here...')"
|
:label="t('Add note here...')"
|
||||||
filled
|
filled
|
||||||
size="lg"
|
size="lg"
|
||||||
autogrow
|
autogrow
|
||||||
v-model="newNote"
|
autofocus
|
||||||
></QInput>
|
@keyup.ctrl.enter.stop="insert"
|
||||||
</QCardSection>
|
clearable
|
||||||
<QCardActions class="justify-end q-mr-sm">
|
>
|
||||||
<QBtn
|
<template #append
|
||||||
|
><QBtn
|
||||||
|
:title="t('Save (ctrl + Enter)')"
|
||||||
|
icon="save"
|
||||||
|
color="primary"
|
||||||
flat
|
flat
|
||||||
:label="t('globals.close')"
|
|
||||||
color="primary"
|
|
||||||
v-close-popup
|
|
||||||
/>
|
|
||||||
<QBtn
|
|
||||||
:label="t('globals.save')"
|
|
||||||
color="primary"
|
|
||||||
v-close-popup
|
|
||||||
@click="insert"
|
@click="insert"
|
||||||
/>
|
/>
|
||||||
</QCardActions>
|
</template>
|
||||||
|
</QInput>
|
||||||
|
</QCardSection>
|
||||||
</QCard>
|
</QCard>
|
||||||
</QDialog>
|
<VnPaginate
|
||||||
|
:data-key="$props.url"
|
||||||
|
:url="$props.url"
|
||||||
|
order="created DESC"
|
||||||
|
:limit="0"
|
||||||
|
:filter="$props.filter"
|
||||||
|
auto-load
|
||||||
|
ref="vnPaginateRef"
|
||||||
|
class="show"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<TransitionGroup name="list" tag="div" class="column items-center full-width">
|
||||||
|
<QCard
|
||||||
|
class="q-pa-xs q-mb-sm full-width"
|
||||||
|
v-for="(note, index) in rows"
|
||||||
|
:key="note.id ?? index"
|
||||||
|
>
|
||||||
|
<QCardSection horizontal>
|
||||||
|
<VnAvatar
|
||||||
|
:descriptor="false"
|
||||||
|
:worker-id="note.workerFk"
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<div class="full-width row justify-between q-pa-xs">
|
||||||
|
<VnUserLink
|
||||||
|
:name="`${note.worker.user.nickname}`"
|
||||||
|
:worker-id="note.worker.id"
|
||||||
|
/>
|
||||||
|
{{ toDateHourMin(note.created) }}
|
||||||
</div>
|
</div>
|
||||||
|
</QCardSection>
|
||||||
|
<QCardSection class="q-pa-xs q-my-none q-py-none">
|
||||||
|
{{ note.text }}
|
||||||
|
</QCardSection>
|
||||||
|
</QCard>
|
||||||
|
</TransitionGroup>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.q-card {
|
.q-card {
|
||||||
|
@ -128,9 +116,20 @@ async function insert() {
|
||||||
.q-dialog .q-card {
|
.q-dialog .q-card {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
.list-enter-active,
|
||||||
|
.list-leave-active {
|
||||||
|
transition: all 1s ease;
|
||||||
|
}
|
||||||
|
.list-enter-from,
|
||||||
|
.list-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
background-color: $primary;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Add note here...: Añadir nota aquí...
|
Add note here...: Añadir nota aquí...
|
||||||
Add note: Añadir nota
|
New note: Nueva nota
|
||||||
|
Save (ctrl + Enter): Guardar (Ctrl + Intro)
|
||||||
|
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -61,7 +61,6 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onPaginate']);
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
||||||
defineExpose({ fetch });
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const pagination = ref({
|
const pagination = ref({
|
||||||
sortBy: props.order,
|
sortBy: props.order,
|
||||||
|
@ -91,9 +90,15 @@ watch(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const addFilter = async (filter, params) => {
|
||||||
|
await arrayData.addFilter({ filter, params });
|
||||||
|
};
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
|
store.filter.skip = 0;
|
||||||
|
store.skip = 0;
|
||||||
await arrayData.fetch({ append: false });
|
await arrayData.fetch({ append: false });
|
||||||
if (!arrayData.hasMoreData.value) {
|
if (!store.hasMoreData) {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
emit('onFetch', store.data);
|
emit('onFetch', store.data);
|
||||||
|
@ -106,11 +111,10 @@ async function paginate() {
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await arrayData.loadMore();
|
await arrayData.loadMore();
|
||||||
|
if (!store.hasMoreData) {
|
||||||
if (!arrayData.hasMoreData.value) {
|
if (store.userParamsChanged) store.hasMoreData = true;
|
||||||
if (store.userParamsChanged) arrayData.hasMoreData.value = true;
|
|
||||||
store.userParamsChanged = false;
|
store.userParamsChanged = false;
|
||||||
isLoading.value = false;
|
endPagination();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,16 +124,16 @@ async function paginate() {
|
||||||
pagination.value.sortBy = sortBy;
|
pagination.value.sortBy = sortBy;
|
||||||
pagination.value.descending = descending;
|
pagination.value.descending = descending;
|
||||||
|
|
||||||
isLoading.value = false;
|
endPagination();
|
||||||
|
}
|
||||||
|
|
||||||
|
function endPagination() {
|
||||||
|
isLoading.value = false;
|
||||||
emit('onFetch', store.data);
|
emit('onFetch', store.data);
|
||||||
emit('onPaginate');
|
emit('onPaginate');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onLoad(index, done) {
|
async function onLoad(index, done) {
|
||||||
if (!store.data) {
|
if (!store.data) return done();
|
||||||
return done();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (store.data.length === 0 || !props.url) return done(false);
|
if (store.data.length === 0 || !props.url) return done(false);
|
||||||
|
|
||||||
|
@ -137,9 +141,11 @@ async function onLoad(index, done) {
|
||||||
|
|
||||||
await paginate();
|
await paginate();
|
||||||
let isDone = false;
|
let isDone = false;
|
||||||
if (store.userParamsChanged) isDone = !arrayData.hasMoreData.value;
|
if (store.userParamsChanged) isDone = !store.hasMoreData;
|
||||||
done(isDone);
|
done(isDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineExpose({ fetch, addFilter });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -179,8 +185,8 @@ async function onLoad(index, done) {
|
||||||
v-if="store.data"
|
v-if="store.data"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
:offset="offset"
|
:offset="offset"
|
||||||
:disable="disableInfiniteScroll || !arrayData.hasMoreData.value"
|
|
||||||
class="full-width"
|
class="full-width"
|
||||||
|
:disable="disableInfiniteScroll || !store.hasMoreData"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
>
|
>
|
||||||
<slot name="body" :rows="store.data"></slot>
|
<slot name="body" :rows="store.data"></slot>
|
||||||
|
@ -188,6 +194,12 @@ async function onLoad(index, done) {
|
||||||
<QSpinner color="orange" size="md" />
|
<QSpinner color="orange" size="md" />
|
||||||
</div>
|
</div>
|
||||||
</QInfiniteScroll>
|
</QInfiniteScroll>
|
||||||
|
<div
|
||||||
|
v-if="!isLoading && store.hasMoreData"
|
||||||
|
class="w-full flex justify-center q-mt-md"
|
||||||
|
>
|
||||||
|
<QBtn color="primary" :label="t('Load more data')" @click="paginate()" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -204,4 +216,5 @@ async function onLoad(index, done) {
|
||||||
es:
|
es:
|
||||||
No data to display: Sin datos que mostrar
|
No data to display: Sin datos que mostrar
|
||||||
No results found: No se han encontrado resultados
|
No results found: No se han encontrado resultados
|
||||||
|
Load more data: Cargar más resultados
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="row" class="q-gutter-md q-mb-md">
|
<div class="vn-row q-gutter-md q-mb-md">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scopped>
|
<style lang="scss" scopped>
|
||||||
#row {
|
.vn-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
> * {
|
> * {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 800px) {
|
@media screen and (max-width: 800px) {
|
||||||
#row {
|
.vn-row {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
|
||||||
|
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
@ -68,9 +66,8 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
|
||||||
const arrayData = useArrayData(props.dataKey, { ...props });
|
const arrayData = useArrayData(props.dataKey, { ...props });
|
||||||
const store = arrayData.store;
|
const { store } = arrayData;
|
||||||
const searchText = ref('');
|
const searchText = ref('');
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -84,31 +81,42 @@ async function search() {
|
||||||
const staticParams = Object.entries(store.userParams).filter(
|
const staticParams = Object.entries(store.userParams).filter(
|
||||||
([key, value]) => value && (props.staticParams || []).includes(key)
|
([key, value]) => value && (props.staticParams || []).includes(key)
|
||||||
);
|
);
|
||||||
|
// const filter =props?.where? { where: JSON.parse(props.where) }: {}
|
||||||
await arrayData.applyFilter({
|
await arrayData.applyFilter({
|
||||||
params: {
|
params: {
|
||||||
|
// filter ,
|
||||||
...Object.fromEntries(staticParams),
|
...Object.fromEntries(staticParams),
|
||||||
search: searchText.value,
|
search: searchText.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!props.redirect) return;
|
if (!props.redirect) return;
|
||||||
|
|
||||||
if (props.customRouteRedirectName) {
|
if (props.customRouteRedirectName)
|
||||||
router.push({
|
return router.push({
|
||||||
name: props.customRouteRedirectName,
|
name: props.customRouteRedirectName,
|
||||||
params: { id: searchText.value },
|
params: { id: searchText.value },
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { matched: matches } = route;
|
const { matched: matches } = router.currentRoute.value;
|
||||||
const { path } = matches[matches.length - 1];
|
const { path } = matches.at(-1);
|
||||||
const newRoute = path.replace(':id', searchText.value);
|
const [, moduleName] = path.split('/');
|
||||||
await router.push(newRoute);
|
|
||||||
|
if (!store.data.length || store.data.length > 1)
|
||||||
|
return router.push({ path: `/${moduleName}/list` });
|
||||||
|
|
||||||
|
const targetId = store.data[0].id;
|
||||||
|
let targetUrl;
|
||||||
|
|
||||||
|
if (path.endsWith('/list')) targetUrl = path.replace('/list', `/${targetId}/summary`);
|
||||||
|
if (path.endsWith('-list')) targetUrl = path.replace('-list', `/${targetId}/summary`);
|
||||||
|
else if (path.includes(':id')) targetUrl = path.replace(':id', targetId);
|
||||||
|
|
||||||
|
await router.push({ path: targetUrl });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QForm @submit="search">
|
<QForm @submit="search" id="searchbarForm">
|
||||||
<VnInput
|
<VnInput
|
||||||
id="searchbar"
|
id="searchbar"
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
|
@ -126,13 +134,6 @@ async function search() {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QIcon
|
|
||||||
v-if="searchText !== ''"
|
|
||||||
name="close"
|
|
||||||
@click="searchText = ''"
|
|
||||||
class="cursor-pointer"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="props.info && $q.screen.gt.xs"
|
v-if="props.info && $q.screen.gt.xs"
|
||||||
name="info"
|
name="info"
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
const actions = ref(null);
|
||||||
|
const data = ref(null);
|
||||||
|
const opts = { subtree: true, childList: true, attributes: true };
|
||||||
|
const hasContent = ref(false);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
stateStore.toggleSubToolbar();
|
stateStore.toggleSubToolbar();
|
||||||
|
actions.value = document.querySelector('#st-actions');
|
||||||
|
data.value = document.querySelector('#st-data');
|
||||||
|
|
||||||
|
if (!actions.value && !data.value) return;
|
||||||
|
|
||||||
|
// Check if there's content to display
|
||||||
|
const observer = new MutationObserver(
|
||||||
|
() =>
|
||||||
|
(hasContent.value =
|
||||||
|
actions.value.childNodes.length + data.value.childNodes.length)
|
||||||
|
);
|
||||||
|
if (actions.value) observer.observe(actions.value, opts);
|
||||||
|
if (data.value) observer.observe(data.value, opts);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -14,7 +30,10 @@ onUnmounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QToolbar class="justify-end sticky">
|
<QToolbar
|
||||||
|
class="justify-end sticky"
|
||||||
|
v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
|
||||||
|
>
|
||||||
<slot name="st-data">
|
<slot name="st-data">
|
||||||
<div id="st-data"></div>
|
<div id="st-data"></div>
|
||||||
</slot>
|
</slot>
|
||||||
|
@ -24,6 +43,11 @@ onUnmounted(() => {
|
||||||
</slot>
|
</slot>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
</template>
|
</template>
|
||||||
|
<style lang="scss">
|
||||||
|
.q-toolbar {
|
||||||
|
background: var(--vn-section-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.sticky {
|
.sticky {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
|
@ -76,7 +76,7 @@ const removeNode = (node) => {
|
||||||
notify(t('department.departmentRemoved'), 'positive');
|
notify(t('department.departmentRemoved'), 'positive');
|
||||||
await fetchNodeLeaves(parentFk);
|
await fetchNodeLeaves(parentFk);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Error removing department');
|
console.error('Error removing department');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { getUrl } from './getUrl';
|
||||||
const { getTokenMultimedia } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = getTokenMultimedia();
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
export async function downloadFile(dmsId) {
|
export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) {
|
||||||
let appUrl = await getUrl('', 'lilium');
|
let appUrl = await getUrl('', 'lilium');
|
||||||
appUrl = appUrl.replace('/#/', '');
|
appUrl = appUrl.replace('/#/', '');
|
||||||
window.open(`${appUrl}/api/dms/${dmsId}/downloadFile?access_token=${token}`);
|
window.open(url ?? `${appUrl}/api/${model}/${id}${urlPath}?access_token=${token}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export const elementIsVisibleInViewport = (el) => {
|
||||||
|
const { top, left, bottom, right } = el.getBoundingClientRect();
|
||||||
|
const { innerHeight, innerWidth } = window;
|
||||||
|
|
||||||
|
return top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
export function getDateQBadgeColor(date) {
|
||||||
|
let today = Date.vnNew();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
let timeTicket = new Date(date);
|
||||||
|
timeTicket.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
let comparation = today - timeTicket;
|
||||||
|
|
||||||
|
if (comparation == 0) return 'warning';
|
||||||
|
if (comparation < 0) return 'negative';
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { onMounted, ref, computed } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
||||||
import { buildFilter } from 'filters/filterPanel';
|
import { buildFilter } from 'filters/filterPanel';
|
||||||
|
@ -9,13 +9,9 @@ const arrayDataStore = useArrayDataStore();
|
||||||
export function useArrayData(key, userOptions) {
|
export function useArrayData(key, userOptions) {
|
||||||
if (!key) throw new Error('ArrayData: A key is required to use this composable');
|
if (!key) throw new Error('ArrayData: A key is required to use this composable');
|
||||||
|
|
||||||
if (!arrayDataStore.get(key)) {
|
if (!arrayDataStore.get(key)) arrayDataStore.set(key);
|
||||||
arrayDataStore.set(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = arrayDataStore.get(key);
|
const store = arrayDataStore.get(key);
|
||||||
const hasMoreData = ref(false);
|
|
||||||
const router = useRouter();
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
let canceller = null;
|
let canceller = null;
|
||||||
|
|
||||||
|
@ -23,6 +19,7 @@ export function useArrayData(key, userOptions) {
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setOptions();
|
setOptions();
|
||||||
|
store.skip = 0;
|
||||||
|
|
||||||
const query = route.query;
|
const query = route.query;
|
||||||
if (query.params) {
|
if (query.params) {
|
||||||
|
@ -30,9 +27,7 @@ export function useArrayData(key, userOptions) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (key && userOptions) {
|
if (key && userOptions) setOptions();
|
||||||
setOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setOptions() {
|
function setOptions() {
|
||||||
const allowedOptions = [
|
const allowedOptions = [
|
||||||
|
@ -97,15 +92,14 @@ export function useArrayData(key, userOptions) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const { limit } = filter;
|
const { limit } = filter;
|
||||||
|
store.hasMoreData = limit && response.data.length >= limit;
|
||||||
hasMoreData.value = response.data.length === limit;
|
|
||||||
|
|
||||||
if (append) {
|
if (append) {
|
||||||
if (!store.data) store.data = [];
|
if (!store.data) store.data = [];
|
||||||
for (const row of response.data) store.data.push(row);
|
for (const row of response.data) store.data.push(row);
|
||||||
} else {
|
} else {
|
||||||
store.data = response.data;
|
store.data = response.data;
|
||||||
if (!document.querySelectorAll('[role="dialog"]'))
|
if (!document.querySelectorAll('[role="dialog"]').length)
|
||||||
updateRouter && updateStateParams();
|
updateRouter && updateStateParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +139,8 @@ export function useArrayData(key, userOptions) {
|
||||||
store.userParams = userParams;
|
store.userParams = userParams;
|
||||||
store.skip = 0;
|
store.skip = 0;
|
||||||
store.filter.skip = 0;
|
store.filter.skip = 0;
|
||||||
|
page.value = 1;
|
||||||
|
|
||||||
await fetch({ append: false });
|
await fetch({ append: false });
|
||||||
return { filter, params };
|
return { filter, params };
|
||||||
}
|
}
|
||||||
|
@ -155,9 +151,10 @@ export function useArrayData(key, userOptions) {
|
||||||
delete store.userParams[param];
|
delete store.userParams[param];
|
||||||
delete params[param];
|
delete params[param];
|
||||||
if (store.filter?.where) {
|
if (store.filter?.where) {
|
||||||
delete store.filter.where[
|
const key = Object.keys(
|
||||||
Object.keys(exprBuilder ? exprBuilder(param) : param)[0]
|
exprBuilder && exprBuilder(param) ? exprBuilder(param) : param
|
||||||
];
|
);
|
||||||
|
if (key[0]) delete store.filter.where[key[0]];
|
||||||
if (Object.keys(store.filter.where).length === 0) {
|
if (Object.keys(store.filter.where).length === 0) {
|
||||||
delete store.filter.where;
|
delete store.filter.where;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +165,7 @@ export function useArrayData(key, userOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMore() {
|
async function loadMore() {
|
||||||
if (!hasMoreData.value) return;
|
if (!store.hasMoreData) return;
|
||||||
|
|
||||||
store.skip = store.limit * page.value;
|
store.skip = store.limit * page.value;
|
||||||
page.value += 1;
|
page.value += 1;
|
||||||
|
@ -188,11 +185,15 @@ export function useArrayData(key, userOptions) {
|
||||||
if (store.userParams && Object.keys(store.userParams).length !== 0)
|
if (store.userParams && Object.keys(store.userParams).length !== 0)
|
||||||
query.params = JSON.stringify(store.userParams);
|
query.params = JSON.stringify(store.userParams);
|
||||||
|
|
||||||
if (router)
|
const url = new URL(window.location.href);
|
||||||
router.replace({
|
const { hash: currentHash } = url;
|
||||||
path: route.path,
|
const [currentRoute] = currentHash.split('?');
|
||||||
query: query,
|
|
||||||
});
|
const params = new URLSearchParams();
|
||||||
|
for (const param in query) params.append(param, query[param]);
|
||||||
|
|
||||||
|
url.hash = currentRoute + '?' + params.toString();
|
||||||
|
window.history.pushState({}, '', url.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalRows = computed(() => (store.data && store.data.length) || 0);
|
const totalRows = computed(() => (store.data && store.data.length) || 0);
|
||||||
|
@ -206,7 +207,6 @@ export function useArrayData(key, userOptions) {
|
||||||
destroy,
|
destroy,
|
||||||
loadMore,
|
loadMore,
|
||||||
store,
|
store,
|
||||||
hasMoreData,
|
|
||||||
totalRows,
|
totalRows,
|
||||||
updateStateParams,
|
updateStateParams,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
export function usePrintService() {
|
export function usePrintService() {
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { getToken } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
|
|
||||||
function sendEmail(path, params) {
|
function sendEmail(path, params) {
|
||||||
return axios.post(path, params).then(() =>
|
return axios.post(path, params).then(() =>
|
||||||
|
@ -19,7 +19,7 @@ export function usePrintService() {
|
||||||
function openReport(path, params) {
|
function openReport(path, params) {
|
||||||
params = Object.assign(
|
params = Object.assign(
|
||||||
{
|
{
|
||||||
access_token: getToken(),
|
access_token: getTokenMultimedia(),
|
||||||
},
|
},
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,31 +3,46 @@ import { useRole } from './useRole';
|
||||||
import { useUserConfig } from './useUserConfig';
|
import { useUserConfig } from './useUserConfig';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import useNotify from './useNotify';
|
import useNotify from './useNotify';
|
||||||
|
import { useTokenConfig } from './useTokenConfig';
|
||||||
|
const TOKEN_MULTIMEDIA = 'tokenMultimedia';
|
||||||
|
const TOKEN = 'token';
|
||||||
|
|
||||||
export function useSession() {
|
export function useSession() {
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
let isCheckingToken = false;
|
||||||
|
let intervalId = null;
|
||||||
|
|
||||||
function getToken() {
|
function getToken() {
|
||||||
const localToken = localStorage.getItem('token');
|
const localToken = localStorage.getItem(TOKEN);
|
||||||
const sessionToken = sessionStorage.getItem('token');
|
const sessionToken = sessionStorage.getItem(TOKEN);
|
||||||
|
|
||||||
return localToken || sessionToken || '';
|
return localToken || sessionToken || '';
|
||||||
}
|
}
|
||||||
function getTokenMultimedia() {
|
function getTokenMultimedia() {
|
||||||
const localTokenMultimedia = localStorage.getItem('tokenMultimedia');
|
const localTokenMultimedia = localStorage.getItem(TOKEN_MULTIMEDIA);
|
||||||
const sessionTokenMultimedia = sessionStorage.getItem('tokenMultimedia');
|
const sessionTokenMultimedia = sessionStorage.getItem(TOKEN_MULTIMEDIA);
|
||||||
|
|
||||||
return localTokenMultimedia || sessionTokenMultimedia || '';
|
return localTokenMultimedia || sessionTokenMultimedia || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setToken(data) {
|
function setSession(data) {
|
||||||
if (data.keepLogin) {
|
let keepLogin = data.keepLogin;
|
||||||
localStorage.setItem('token', data.token);
|
const storage = keepLogin ? localStorage : sessionStorage;
|
||||||
localStorage.setItem('tokenMultimedia', data.tokenMultimedia);
|
storage.setItem(TOKEN, data.token);
|
||||||
} else {
|
storage.setItem(TOKEN_MULTIMEDIA, data.tokenMultimedia);
|
||||||
sessionStorage.setItem('token', data.token);
|
storage.setItem('created', data.created);
|
||||||
sessionStorage.setItem('tokenMultimedia', data.tokenMultimedia);
|
storage.setItem('ttl', data.ttl);
|
||||||
|
sessionStorage.setItem('keepLogin', keepLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keepLogin() {
|
||||||
|
return sessionStorage.getItem('keepLogin');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setToken({ token, tokenMultimedia }) {
|
||||||
|
const storage = keepLogin() ? localStorage : sessionStorage;
|
||||||
|
storage.setItem(TOKEN, token);
|
||||||
|
storage.setItem(TOKEN_MULTIMEDIA, tokenMultimedia);
|
||||||
}
|
}
|
||||||
async function destroyToken(url, storage, key) {
|
async function destroyToken(url, storage, key) {
|
||||||
if (storage.getItem(key)) {
|
if (storage.getItem(key)) {
|
||||||
|
@ -47,11 +62,15 @@ export function useSession() {
|
||||||
tokenMultimedia: 'Accounts/logout',
|
tokenMultimedia: 'Accounts/logout',
|
||||||
token: 'VnUsers/logout',
|
token: 'VnUsers/logout',
|
||||||
};
|
};
|
||||||
|
const storage = keepLogin() ? localStorage : sessionStorage;
|
||||||
|
|
||||||
for (const [key, url] of Object.entries(tokens)) {
|
for (const [key, url] of Object.entries(tokens)) {
|
||||||
await destroyToken(url, localStorage, key);
|
await destroyToken(url, storage, key);
|
||||||
await destroyToken(url, sessionStorage, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
localStorage.clear();
|
||||||
|
sessionStorage.clear();
|
||||||
|
|
||||||
const { setUser } = useState();
|
const { setUser } = useState();
|
||||||
|
|
||||||
setUser({
|
setUser({
|
||||||
|
@ -61,22 +80,75 @@ export function useSession() {
|
||||||
lang: '',
|
lang: '',
|
||||||
darkMode: null,
|
darkMode: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stopRenewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function login(token, tokenMultimedia, keepLogin) {
|
async function login(data) {
|
||||||
setToken({ token, tokenMultimedia, keepLogin });
|
setSession(data);
|
||||||
|
|
||||||
await useRole().fetch();
|
await useRole().fetch();
|
||||||
await useUserConfig().fetch();
|
await useUserConfig().fetch();
|
||||||
|
await useTokenConfig().fetch();
|
||||||
|
|
||||||
|
startInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLoggedIn() {
|
function isLoggedIn() {
|
||||||
const localToken = localStorage.getItem('token');
|
const localToken = localStorage.getItem(TOKEN);
|
||||||
const sessionToken = sessionStorage.getItem('token');
|
const sessionToken = sessionStorage.getItem(TOKEN);
|
||||||
|
startInterval();
|
||||||
return !!(localToken || sessionToken);
|
return !!(localToken || sessionToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startInterval() {
|
||||||
|
stopRenewer();
|
||||||
|
const renewPeriod = +sessionStorage.getItem('renewPeriod');
|
||||||
|
if (!renewPeriod) return;
|
||||||
|
intervalId = setInterval(() => checkValidity(), renewPeriod * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopRenewer() {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renewToken() {
|
||||||
|
const _token = getToken();
|
||||||
|
const token = await axios.post('VnUsers/renewToken', {
|
||||||
|
headers: { Authorization: _token },
|
||||||
|
});
|
||||||
|
const _tokenMultimedia = getTokenMultimedia();
|
||||||
|
const tokenMultimedia = await axios.post('VnUsers/renewToken', {
|
||||||
|
headers: { Authorization: _tokenMultimedia },
|
||||||
|
});
|
||||||
|
setToken({ token: token.data.id, tokenMultimedia: tokenMultimedia.data.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkValidity() {
|
||||||
|
const { getTokenConfig } = useState();
|
||||||
|
|
||||||
|
const tokenConfig = getTokenConfig() ?? sessionStorage.getItem('tokenConfig');
|
||||||
|
const storage = keepLogin() ? localStorage : sessionStorage;
|
||||||
|
|
||||||
|
const created = +storage.getItem('created');
|
||||||
|
const ttl = +storage.getItem('ttl');
|
||||||
|
|
||||||
|
if (isCheckingToken || !created) return;
|
||||||
|
isCheckingToken = true;
|
||||||
|
|
||||||
|
const renewPeriodInSeconds = Math.min(ttl, tokenConfig.value.renewPeriod) * 1000;
|
||||||
|
|
||||||
|
const maxDate = created + renewPeriodInSeconds;
|
||||||
|
const now = new Date().getTime();
|
||||||
|
|
||||||
|
if (isNaN(renewPeriodInSeconds) || now <= maxDate) {
|
||||||
|
return (isCheckingToken = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
await renewToken();
|
||||||
|
isCheckingToken = false;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getToken,
|
getToken,
|
||||||
getTokenMultimedia,
|
getTokenMultimedia,
|
||||||
|
@ -84,5 +156,8 @@ export function useSession() {
|
||||||
destroy,
|
destroy,
|
||||||
login,
|
login,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
|
checkValidity,
|
||||||
|
setSession,
|
||||||
|
renewToken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ const user = ref({
|
||||||
});
|
});
|
||||||
|
|
||||||
const roles = ref([]);
|
const roles = ref([]);
|
||||||
|
const tokenConfig = ref({});
|
||||||
const drawer = ref(true);
|
const drawer = ref(true);
|
||||||
const headerMounted = ref(false);
|
const headerMounted = ref(false);
|
||||||
|
|
||||||
|
@ -52,6 +53,15 @@ export function useState() {
|
||||||
function setRoles(data) {
|
function setRoles(data) {
|
||||||
roles.value = data;
|
roles.value = data;
|
||||||
}
|
}
|
||||||
|
function getTokenConfig() {
|
||||||
|
return computed(() => {
|
||||||
|
return tokenConfig.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenConfig(data) {
|
||||||
|
tokenConfig.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
function set(name, data) {
|
function set(name, data) {
|
||||||
state.value[name] = ref(data);
|
state.value[name] = ref(data);
|
||||||
|
@ -70,6 +80,8 @@ export function useState() {
|
||||||
setUser,
|
setUser,
|
||||||
getRoles,
|
getRoles,
|
||||||
setRoles,
|
setRoles,
|
||||||
|
getTokenConfig,
|
||||||
|
setTokenConfig,
|
||||||
set,
|
set,
|
||||||
get,
|
get,
|
||||||
unset,
|
unset,
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useState } from './useState';
|
||||||
|
import useNotify from './useNotify';
|
||||||
|
|
||||||
|
export function useTokenConfig() {
|
||||||
|
const state = useState();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
async function fetch() {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get('AccessTokenConfigs/findOne', {
|
||||||
|
filter: { fields: ['renewInterval', 'renewPeriod'] },
|
||||||
|
});
|
||||||
|
if (!data) return;
|
||||||
|
state.setTokenConfig(data);
|
||||||
|
sessionStorage.setItem('renewPeriod', data.renewPeriod);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
notify('errors.tokenConfig', 'negative');
|
||||||
|
console.error('Error fetching token config:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetch,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
// app global css in SCSS form
|
// app global css in SCSS form
|
||||||
@import './icons.scss';
|
@import './icons.scss';
|
||||||
|
@import '@quasar/quasar-ui-qcalendar/src/QCalendarMonth.sass';
|
||||||
|
|
||||||
body.body--light {
|
body.body--light {
|
||||||
--font-color: black;
|
--font-color: black;
|
||||||
|
@ -14,21 +15,15 @@ body.body--light {
|
||||||
.q-header .q-toolbar {
|
.q-header .q-toolbar {
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
}
|
}
|
||||||
.q-card,
|
|
||||||
.q-table,
|
|
||||||
.q-table__bottom,
|
|
||||||
.q-drawer {
|
|
||||||
background-color: var(--vn-section-color);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
body.body--dark {
|
body.body--dark {
|
||||||
--vn-section-color: #403c3c;
|
--vn-page-color: #222;
|
||||||
|
--vn-section-color: #3d3d3d;
|
||||||
--vn-text-color: white;
|
--vn-text-color: white;
|
||||||
--vn-label-color: #a8a8a8;
|
--vn-label-color: #a8a8a8;
|
||||||
--vn-accent-color: #424242;
|
--vn-accent-color: #424242;
|
||||||
|
|
||||||
background-color: #222;
|
background-color: var(--vn-page-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -76,6 +71,13 @@ select:-webkit-autofill {
|
||||||
.bg-vn-section-color {
|
.bg-vn-section-color {
|
||||||
background-color: var(--vn-section-color);
|
background-color: var(--vn-section-color);
|
||||||
}
|
}
|
||||||
|
.bg-hover {
|
||||||
|
background-color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-vn-label {
|
||||||
|
color: var(--vn-label);
|
||||||
|
}
|
||||||
|
|
||||||
.color-vn-text {
|
.color-vn-text {
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
|
@ -85,12 +87,21 @@ select:-webkit-autofill {
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-width {
|
||||||
|
max-width: 800px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.vn-card {
|
.vn-card {
|
||||||
background-color: var(--vn-section-color);
|
background-color: var(--vn-section-color);
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-width {
|
||||||
|
width: 770px;
|
||||||
|
}
|
||||||
|
|
||||||
.vn-card-list {
|
.vn-card-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 60em;
|
max-width: 60em;
|
||||||
|
@ -108,6 +119,11 @@ select:-webkit-autofill {
|
||||||
font-variation-settings: 'FILL' 1;
|
font-variation-settings: 'FILL' 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fill-icon-on-hover:hover {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.vn-table-separation-row {
|
.vn-table-separation-row {
|
||||||
height: 16px !important;
|
height: 16px !important;
|
||||||
background-color: var(--vn-section-color) !important;
|
background-color: var(--vn-section-color) !important;
|
||||||
|
@ -118,9 +134,40 @@ select:-webkit-autofill {
|
||||||
content: ' *';
|
content: ' *';
|
||||||
}
|
}
|
||||||
|
|
||||||
.q-chip {
|
.q-card,
|
||||||
|
.q-table,
|
||||||
|
.q-table__bottom,
|
||||||
|
.q-drawer {
|
||||||
|
background-color: var(--vn-section-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tr-header {
|
||||||
|
color: var(--vn-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-chip,
|
||||||
|
.q-notification__message,
|
||||||
|
.q-notification__icon {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
.q-notification--standard.bg-negative {
|
||||||
|
background-color: #fa3939 !important;
|
||||||
|
}
|
||||||
|
.q-notification--standard.bg-positive {
|
||||||
|
background-color: #a3d131 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-tooltip {
|
||||||
|
background-color: var(--vn-page-color);
|
||||||
|
color: var(--font-color);
|
||||||
|
font-size: medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-card__actions {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* q-notification row items-stretch q-notification--standard bg-negative text-white */
|
||||||
|
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
|
@ -131,3 +178,7 @@ input::-webkit-inner-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.q-scrollarea__content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
<glyph unicode="" glyph-name="consignatarios" d="M409.6-64v349.867h204.8v-349.867h256v563.2h153.6l-512 460.8-512-460.8h153.6v-563.2h256z" />
|
<glyph unicode="" glyph-name="consignatarios" d="M409.6-64v349.867h204.8v-349.867h256v563.2h153.6l-512 460.8-512-460.8h153.6v-563.2h256z" />
|
||||||
<glyph unicode="" glyph-name="control" d="M418.133 315.733l-128-123.733 256-256 469.333 469.333-128 128-341.333-341.333zM546.133 311.467l34.133 34.133h-68.267zM230.4 128l-59.733 64 153.6 153.6h-68.267v102.4h426.667l204.8 204.8 85.333-85.333v187.733c0 55.467-46.933 102.4-102.4 102.4h-213.333c-21.333 59.733-76.8 102.4-145.067 102.4s-123.733-42.667-145.067-102.4h-213.333c-55.467 0-102.4-46.933-102.4-102.4v-716.8c0-55.467 46.933-102.4 102.4-102.4h273.067l-196.267 192zM512 857.6c29.867 0 51.2-21.333 51.2-51.2s-21.333-51.2-51.2-51.2-51.2 21.333-51.2 51.2c0 29.867 21.333 51.2 51.2 51.2zM256 652.8h512v-102.4h-512v102.4zM665.6-64h204.8c55.467 0 102.4 46.933 102.4 102.4v204.8l-307.2-307.2z" />
|
<glyph unicode="" glyph-name="control" d="M418.133 315.733l-128-123.733 256-256 469.333 469.333-128 128-341.333-341.333zM546.133 311.467l34.133 34.133h-68.267zM230.4 128l-59.733 64 153.6 153.6h-68.267v102.4h426.667l204.8 204.8 85.333-85.333v187.733c0 55.467-46.933 102.4-102.4 102.4h-213.333c-21.333 59.733-76.8 102.4-145.067 102.4s-123.733-42.667-145.067-102.4h-213.333c-55.467 0-102.4-46.933-102.4-102.4v-716.8c0-55.467 46.933-102.4 102.4-102.4h273.067l-196.267 192zM512 857.6c29.867 0 51.2-21.333 51.2-51.2s-21.333-51.2-51.2-51.2-51.2 21.333-51.2 51.2c0 29.867 21.333 51.2 51.2 51.2zM256 652.8h512v-102.4h-512v102.4zM665.6-64h204.8c55.467 0 102.4 46.933 102.4 102.4v204.8l-307.2-307.2z" />
|
||||||
<glyph unicode="" glyph-name="credit" d="M921.6 849.067h-819.2c-55.467 0-102.4-42.667-102.4-98.133v-601.6c0-55.467 46.933-102.4 102.4-102.4h819.2c55.467 0 102.4 42.667 102.4 102.4v601.6c0 55.467-46.933 98.133-102.4 98.133zM921.6 145.067h-819.2v302.933h819.2v-302.933zM921.6 648.533h-819.2v102.4h819.2v-102.4z" />
|
<glyph unicode="" glyph-name="credit" d="M921.6 849.067h-819.2c-55.467 0-102.4-42.667-102.4-98.133v-601.6c0-55.467 46.933-102.4 102.4-102.4h819.2c55.467 0 102.4 42.667 102.4 102.4v601.6c0 55.467-46.933 98.133-102.4 98.133zM921.6 145.067h-819.2v302.933h819.2v-302.933zM921.6 648.533h-819.2v102.4h819.2v-102.4z" />
|
||||||
<glyph unicode="" glyph-name="deaulter" d="M677.973-64c-30.72 35.84-61.867 70.827-91.307 107.52-40.96 51.2-80.64 103.253-121.173 154.88-16.64 21.333-21.76 20.48-30.72-4.693-13.227-36.693-25.6-73.387-40.107-109.653-5.12-12.8-13.227-26.88-24.32-34.56-51.627-34.987-104.107-69.12-157.867-100.693-10.667-6.4-30.72-5.547-41.813 0.853-8.107 4.693-12.373 23.893-11.093 35.84 0.853 8.96 11.093 19.627 19.627 25.6 39.253 26.453 78.933 51.627 119.040 76.8 18.347 11.52 30.293 26.027 35.84 47.787 12.373 48.213 27.307 95.573 39.253 143.36 8.533 33.707 26.88 58.88 56.32 77.227 40.533 25.173 80.64 52.053 120.747 78.507 6.4 4.267 10.24 11.52 15.36 17.493-7.253 2.56-14.933 7.253-22.187 6.827-75.52-6.4-151.467-13.227-226.987-20.48-2.133 0-4.693-0.853-6.827-0.853-22.613-1.707-39.253 10.24-40.96 29.867s12.373 33.707 35.413 35.84c45.227 4.267 90.88 8.96 136.107 12.8 65.707 5.547 131.84 10.667 197.547 15.36 26.027 1.707 53.76-21.76 67.413-55.467 9.813-23.893 5.12-46.080-18.347-65.28-49.92-40.107-100.693-78.933-151.040-118.187-23.040-17.92-23.893-23.467-6.4-46.507 58.453-78.080 116.48-156.587 174.933-234.667 27.307-36.693 25.173-50.773-12.373-75.52-5.12 0-9.813 0-14.080 0zM791.893 649.813c-43.093 1.28-76.373-31.573-77.227-75.52-0.853-44.373 29.44-76.8 72.107-77.653 45.227-1.28 77.653 29.44 78.080 73.813 0.427 45.227-29.44 78.080-72.96 79.36zM671.147 737.707c0-72.107-34.133-136.107-87.467-176.64l-235.52-21.76c-72.107 36.693-122.027 111.787-122.027 198.4 0 122.88 99.84 222.293 222.72 222.293 122.453 0 222.293-99.413 222.293-222.293zM592.213 680.533l-50.347 18.347c-2.133-8.533-5.12-16.213-9.813-22.613-5.12-6.4-10.24-11.947-16.213-17.067-5.973-4.267-12.373-8.107-19.2-11.093s-13.653-4.693-20.053-5.547c-17.92-2.987-33.707-0.427-48.64 6.827s-26.88 18.347-36.693 32.853l76.373 12.373 7.253 32.427-97.28-15.787c-1.28 5.547-2.987 11.093-3.84 16.64l-0.853 4.267 99.413 16.213 7.253 32.427-106.667-17.493c0.853 9.387 2.987 17.493 6.4 26.027 3.84 8.533 8.107 16.213 14.080 23.040 5.547 6.827 12.8 12.373 21.333 17.067s17.92 8.107 28.587 9.813c6.827 1.28 13.227 1.707 20.907 1.28s14.507-1.707 21.333-3.84c6.827-2.133 13.653-5.973 20.053-10.24 5.973-4.693 11.947-11.093 17.493-18.773l38.827 37.973c-13.227 17.92-30.293 31.147-52.053 39.253-21.76 8.533-46.080 10.667-73.387 6.4-19.627-2.987-36.267-9.387-51.2-17.92-14.507-8.533-26.88-19.2-37.547-32-10.667-12.373-18.773-26.027-23.893-40.96-5.547-14.507-8.96-29.867-9.813-45.653l-21.76-3.84-7.253-32.427 29.013 4.693 0.427-2.987c1.28-6.827 2.56-12.8 4.267-18.347l-23.467-3.84-8.107-32.427 43.52 7.253c6.827-13.653 15.787-26.027 26.027-36.693 10.24-11.52 22.187-20.48 35.413-27.733 13.227-7.68 27.307-12.8 42.667-15.787s31.573-3.413 47.36-0.853c12.373 2.133 24.32 5.12 35.84 10.667s22.613 11.52 32.853 19.2c10.24 8.107 18.347 16.64 26.027 26.453 6.827 9.387 12.373 20.48 15.36 32.427z" />
|
<glyph unicode="" glyph-name="defaulter" d="M677.973-64c-30.72 35.84-61.867 70.827-91.307 107.52-40.96 51.2-80.64 103.253-121.173 154.88-16.64 21.333-21.76 20.48-30.72-4.693-13.227-36.693-25.6-73.387-40.107-109.653-5.12-12.8-13.227-26.88-24.32-34.56-51.627-34.987-104.107-69.12-157.867-100.693-10.667-6.4-30.72-5.547-41.813 0.853-8.107 4.693-12.373 23.893-11.093 35.84 0.853 8.96 11.093 19.627 19.627 25.6 39.253 26.453 78.933 51.627 119.040 76.8 18.347 11.52 30.293 26.027 35.84 47.787 12.373 48.213 27.307 95.573 39.253 143.36 8.533 33.707 26.88 58.88 56.32 77.227 40.533 25.173 80.64 52.053 120.747 78.507 6.4 4.267 10.24 11.52 15.36 17.493-7.253 2.56-14.933 7.253-22.187 6.827-75.52-6.4-151.467-13.227-226.987-20.48-2.133 0-4.693-0.853-6.827-0.853-22.613-1.707-39.253 10.24-40.96 29.867s12.373 33.707 35.413 35.84c45.227 4.267 90.88 8.96 136.107 12.8 65.707 5.547 131.84 10.667 197.547 15.36 26.027 1.707 53.76-21.76 67.413-55.467 9.813-23.893 5.12-46.080-18.347-65.28-49.92-40.107-100.693-78.933-151.040-118.187-23.040-17.92-23.893-23.467-6.4-46.507 58.453-78.080 116.48-156.587 174.933-234.667 27.307-36.693 25.173-50.773-12.373-75.52-5.12 0-9.813 0-14.080 0zM791.893 649.813c-43.093 1.28-76.373-31.573-77.227-75.52-0.853-44.373 29.44-76.8 72.107-77.653 45.227-1.28 77.653 29.44 78.080 73.813 0.427 45.227-29.44 78.080-72.96 79.36zM671.147 737.707c0-72.107-34.133-136.107-87.467-176.64l-235.52-21.76c-72.107 36.693-122.027 111.787-122.027 198.4 0 122.88 99.84 222.293 222.72 222.293 122.453 0 222.293-99.413 222.293-222.293zM592.213 680.533l-50.347 18.347c-2.133-8.533-5.12-16.213-9.813-22.613-5.12-6.4-10.24-11.947-16.213-17.067-5.973-4.267-12.373-8.107-19.2-11.093s-13.653-4.693-20.053-5.547c-17.92-2.987-33.707-0.427-48.64 6.827s-26.88 18.347-36.693 32.853l76.373 12.373 7.253 32.427-97.28-15.787c-1.28 5.547-2.987 11.093-3.84 16.64l-0.853 4.267 99.413 16.213 7.253 32.427-106.667-17.493c0.853 9.387 2.987 17.493 6.4 26.027 3.84 8.533 8.107 16.213 14.080 23.040 5.547 6.827 12.8 12.373 21.333 17.067s17.92 8.107 28.587 9.813c6.827 1.28 13.227 1.707 20.907 1.28s14.507-1.707 21.333-3.84c6.827-2.133 13.653-5.973 20.053-10.24 5.973-4.693 11.947-11.093 17.493-18.773l38.827 37.973c-13.227 17.92-30.293 31.147-52.053 39.253-21.76 8.533-46.080 10.667-73.387 6.4-19.627-2.987-36.267-9.387-51.2-17.92-14.507-8.533-26.88-19.2-37.547-32-10.667-12.373-18.773-26.027-23.893-40.96-5.547-14.507-8.96-29.867-9.813-45.653l-21.76-3.84-7.253-32.427 29.013 4.693 0.427-2.987c1.28-6.827 2.56-12.8 4.267-18.347l-23.467-3.84-8.107-32.427 43.52 7.253c6.827-13.653 15.787-26.027 26.027-36.693 10.24-11.52 22.187-20.48 35.413-27.733 13.227-7.68 27.307-12.8 42.667-15.787s31.573-3.413 47.36-0.853c12.373 2.133 24.32 5.12 35.84 10.667s22.613 11.52 32.853 19.2c10.24 8.107 18.347 16.64 26.027 26.453 6.827 9.387 12.373 20.48 15.36 32.427z" />
|
||||||
<glyph unicode="" glyph-name="deletedTicket" d="M160.672 85.696h693.248v639.776c0 0-2.016 234.528-349.696 234.528s-343.552-234.528-343.552-234.528v-639.776zM291.328 652.704h170.976v152.256h102.336v-152.256h171.008v-102.336h-171.008v-356.96h-102.336v356.96h-170.976v102.336zM64 61.056v-123.456h899.008v123.456h-899.008z" />
|
<glyph unicode="" glyph-name="deletedTicket" d="M160.672 85.696h693.248v639.776c0 0-2.016 234.528-349.696 234.528s-343.552-234.528-343.552-234.528v-639.776zM291.328 652.704h170.976v152.256h102.336v-152.256h171.008v-102.336h-171.008v-356.96h-102.336v356.96h-170.976v102.336zM64 61.056v-123.456h899.008v123.456h-899.008z" />
|
||||||
<glyph unicode="" glyph-name="deleteline" d="M354.133 192l-98.133 98.133 157.867 153.6-157.867 157.867 98.133 102.4 157.867-157.867 157.867 153.6 98.133-98.133-157.867-157.867 157.867-153.6-98.133-98.133-157.867 157.867-157.867-157.867zM780.8 507.733l-64-64 59.733-55.467h247.467v119.467h-243.2zM307.2 443.733l-64 64h-243.2v-119.467h251.733l55.467 55.467z" />
|
<glyph unicode="" glyph-name="deleteline" d="M354.133 192l-98.133 98.133 157.867 153.6-157.867 157.867 98.133 102.4 157.867-157.867 157.867 153.6 98.133-98.133-157.867-157.867 157.867-153.6-98.133-98.133-157.867 157.867-157.867-157.867zM780.8 507.733l-64-64 59.733-55.467h247.467v119.467h-243.2zM307.2 443.733l-64 64h-243.2v-119.467h251.733l55.467 55.467z" />
|
||||||
<glyph unicode="" glyph-name="delivery" d="M789.333 264.533c-55.467 0-102.4-46.933-102.4-102.4s46.933-102.4 102.4-102.4 102.4 46.933 102.4 102.4c0 59.733-46.933 102.4-102.4 102.4zM789.333 110.933c-29.867 0-51.2 21.333-51.2 51.2s21.333 51.2 51.2 51.2 51.2-21.333 51.2-51.2c0-25.6-25.6-51.2-51.2-51.2zM251.733 264.533c-55.467 0-102.4-46.933-102.4-102.4s46.933-102.4 102.4-102.4c55.467 0 102.4 46.933 102.4 102.4 0 59.733-46.933 102.4-102.4 102.4zM251.733 110.933c-29.867 0-51.2 21.333-51.2 51.2s21.333 51.2 51.2 51.2c29.867 0 51.2-21.333 51.2-51.2 0-25.6-25.6-51.2-51.2-51.2zM1006.933 537.6l-196.267 192c-12.8 12.8-29.867 17.067-46.933 17.067h-98.133v38.4c0 25.6-21.333 51.2-51.2 51.2h-563.2c-29.867 0-51.2-21.333-51.2-51.2v-554.667c0-29.867 25.6-51.2 51.2-51.2h68.267c8.533 64 64 115.2 132.267 115.2 64 0 123.733-51.2 132.267-115.2h268.8c8.533 64 64 115.2 132.267 115.2s128-51.2 136.533-115.2h51.2c29.867 0 51.2 25.6 51.2 51.2v260.267c0 17.067-8.533 34.133-17.067 46.933zM725.333 682.667c0 4.267 4.267 8.533 8.533 8.533h34.133c0 0 4.267 0 4.267-4.267l153.6-145.067c4.267 0 0-12.8-4.267-12.8h-187.733c-8.533 0-8.533 4.267-8.533 8.533v145.067zM311.467 597.333c0 46.933 29.867 85.333 59.733 93.867 4.267 0 4.267 0 8.533 0l98.133 12.8v-51.2c0-46.933-29.867-85.333-59.733-93.867-4.267 0-4.267 0-8.533 0l-98.133-17.067v55.467zM311.467 516.267l46.933 8.533c17.067 4.267 29.867-17.067 29.867-38.4l4.267-29.867-51.2-4.267c-17.067-4.267-29.867 12.8-29.867 38.4v25.6zM149.333 597.333v51.2l85.333 12.8c34.133 4.267 55.467-25.6 55.467-72.533v-51.2l-85.333-12.8c-34.133 0-59.733 29.867-55.467 72.533zM285.867 512v-38.4c0-34.133-21.333-64-42.667-68.267h-4.267l-72.533-8.533v38.4c0 34.133 21.333 64 42.667 68.267h4.267l72.533 8.533z" />
|
<glyph unicode="" glyph-name="delivery" d="M789.333 264.533c-55.467 0-102.4-46.933-102.4-102.4s46.933-102.4 102.4-102.4 102.4 46.933 102.4 102.4c0 59.733-46.933 102.4-102.4 102.4zM789.333 110.933c-29.867 0-51.2 21.333-51.2 51.2s21.333 51.2 51.2 51.2 51.2-21.333 51.2-51.2c0-25.6-25.6-51.2-51.2-51.2zM251.733 264.533c-55.467 0-102.4-46.933-102.4-102.4s46.933-102.4 102.4-102.4c55.467 0 102.4 46.933 102.4 102.4 0 59.733-46.933 102.4-102.4 102.4zM251.733 110.933c-29.867 0-51.2 21.333-51.2 51.2s21.333 51.2 51.2 51.2c29.867 0 51.2-21.333 51.2-51.2 0-25.6-25.6-51.2-51.2-51.2zM1006.933 537.6l-196.267 192c-12.8 12.8-29.867 17.067-46.933 17.067h-98.133v38.4c0 25.6-21.333 51.2-51.2 51.2h-563.2c-29.867 0-51.2-21.333-51.2-51.2v-554.667c0-29.867 25.6-51.2 51.2-51.2h68.267c8.533 64 64 115.2 132.267 115.2 64 0 123.733-51.2 132.267-115.2h268.8c8.533 64 64 115.2 132.267 115.2s128-51.2 136.533-115.2h51.2c29.867 0 51.2 25.6 51.2 51.2v260.267c0 17.067-8.533 34.133-17.067 46.933zM725.333 682.667c0 4.267 4.267 8.533 8.533 8.533h34.133c0 0 4.267 0 4.267-4.267l153.6-145.067c4.267 0 0-12.8-4.267-12.8h-187.733c-8.533 0-8.533 4.267-8.533 8.533v145.067zM311.467 597.333c0 46.933 29.867 85.333 59.733 93.867 4.267 0 4.267 0 8.533 0l98.133 12.8v-51.2c0-46.933-29.867-85.333-59.733-93.867-4.267 0-4.267 0-8.533 0l-98.133-17.067v55.467zM311.467 516.267l46.933 8.533c17.067 4.267 29.867-17.067 29.867-38.4l4.267-29.867-51.2-4.267c-17.067-4.267-29.867 12.8-29.867 38.4v25.6zM149.333 597.333v51.2l85.333 12.8c34.133 4.267 55.467-25.6 55.467-72.533v-51.2l-85.333-12.8c-34.133 0-59.733 29.867-55.467 72.533zM285.867 512v-38.4c0-34.133-21.333-64-42.667-68.267h-4.267l-72.533-8.533v38.4c0 34.133 21.333 64 42.667 68.267h4.267l72.533 8.533z" />
|
||||||
|
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
File diff suppressed because one or more lines are too long
|
@ -10,7 +10,8 @@
|
||||||
font-display: block;
|
font-display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
[class^="icon-"], [class*=" icon-"] {
|
[class^='icon-'],
|
||||||
|
[class*=' icon-'] {
|
||||||
/* use !important to prevent issues with browser extensions that change fonts */
|
/* use !important to prevent issues with browser extensions that change fonts */
|
||||||
font-family: 'icon' !important;
|
font-family: 'icon' !important;
|
||||||
speak: never;
|
speak: never;
|
||||||
|
@ -26,377 +27,392 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-100:before {
|
.icon-100:before {
|
||||||
content: "\e926";
|
content: '\e926';
|
||||||
}
|
}
|
||||||
.icon-Client_unpaid:before {
|
.icon-Client_unpaid:before {
|
||||||
content: "\e925";
|
content: '\e925';
|
||||||
|
}
|
||||||
|
.icon-Client_unpaid:before {
|
||||||
|
content: '\e925';
|
||||||
}
|
}
|
||||||
.icon-History:before {
|
.icon-History:before {
|
||||||
content: "\e964";
|
content: '\e964';
|
||||||
}
|
}
|
||||||
.icon-Person:before {
|
.icon-Person:before {
|
||||||
content: "\e984";
|
content: '\e984';
|
||||||
}
|
}
|
||||||
.icon-accessory:before {
|
.icon-accessory:before {
|
||||||
content: "\e948";
|
content: '\e948';
|
||||||
}
|
}
|
||||||
.icon-account:before {
|
.icon-account:before {
|
||||||
content: "\e927";
|
content: '\e927';
|
||||||
}
|
}
|
||||||
.icon-actions:before {
|
.icon-actions:before {
|
||||||
content: "\e928";
|
content: '\e928';
|
||||||
}
|
}
|
||||||
.icon-addperson:before {
|
.icon-addperson:before {
|
||||||
content: "\e929";
|
content: '\e929';
|
||||||
}
|
}
|
||||||
.icon-agency:before {
|
.icon-agency:before {
|
||||||
content: "\e92a";
|
content: '\e92a';
|
||||||
|
}
|
||||||
|
.icon-agency:before {
|
||||||
|
content: '\e92a';
|
||||||
}
|
}
|
||||||
.icon-agency-term:before {
|
.icon-agency-term:before {
|
||||||
content: "\e92b";
|
content: '\e92b';
|
||||||
}
|
}
|
||||||
.icon-albaran:before {
|
.icon-albaran:before {
|
||||||
content: "\e92c";
|
content: '\e92c';
|
||||||
|
}
|
||||||
|
.icon-albaran:before {
|
||||||
|
content: '\e92c';
|
||||||
}
|
}
|
||||||
.icon-anonymous:before {
|
.icon-anonymous:before {
|
||||||
content: "\e92d";
|
content: '\e92d';
|
||||||
}
|
}
|
||||||
.icon-apps:before {
|
.icon-apps:before {
|
||||||
content: "\e92e";
|
content: '\e92e';
|
||||||
}
|
}
|
||||||
.icon-artificial:before {
|
.icon-artificial:before {
|
||||||
content: "\e92f";
|
content: '\e92f';
|
||||||
}
|
}
|
||||||
.icon-attach:before {
|
.icon-attach:before {
|
||||||
content: "\e930";
|
content: '\e930';
|
||||||
}
|
}
|
||||||
.icon-barcode:before {
|
.icon-barcode:before {
|
||||||
content: "\e932";
|
content: '\e932';
|
||||||
}
|
}
|
||||||
.icon-basket:before {
|
.icon-basket:before {
|
||||||
content: "\e933";
|
content: '\e933';
|
||||||
}
|
}
|
||||||
.icon-basketadd:before {
|
.icon-basketadd:before {
|
||||||
content: "\e934";
|
content: '\e934';
|
||||||
}
|
}
|
||||||
.icon-bin:before {
|
.icon-bin:before {
|
||||||
content: "\e935";
|
content: '\e935';
|
||||||
}
|
}
|
||||||
.icon-botanical:before {
|
.icon-botanical:before {
|
||||||
content: "\e936";
|
content: '\e936';
|
||||||
}
|
}
|
||||||
.icon-bucket:before {
|
.icon-bucket:before {
|
||||||
content: "\e937";
|
content: '\e937';
|
||||||
}
|
}
|
||||||
.icon-buscaman:before {
|
.icon-buscaman:before {
|
||||||
content: "\e938";
|
content: '\e938';
|
||||||
}
|
}
|
||||||
.icon-buyrequest:before {
|
.icon-buyrequest:before {
|
||||||
content: "\e939";
|
content: '\e939';
|
||||||
}
|
}
|
||||||
.icon-calc_volum:before {
|
.icon-calc_volum:before {
|
||||||
content: "\e93a";
|
content: '\e93a';
|
||||||
}
|
}
|
||||||
.icon-calendar:before {
|
.icon-calendar:before {
|
||||||
content: "\e940";
|
content: '\e940';
|
||||||
}
|
}
|
||||||
.icon-catalog:before {
|
.icon-catalog:before {
|
||||||
content: "\e941";
|
content: '\e941';
|
||||||
}
|
}
|
||||||
.icon-claims:before {
|
.icon-claims:before {
|
||||||
content: "\e942";
|
content: '\e942';
|
||||||
}
|
}
|
||||||
.icon-client:before {
|
.icon-client:before {
|
||||||
content: "\e943";
|
content: '\e943';
|
||||||
}
|
}
|
||||||
.icon-clone:before {
|
.icon-clone:before {
|
||||||
content: "\e945";
|
content: '\e945';
|
||||||
}
|
}
|
||||||
.icon-columnadd:before {
|
.icon-columnadd:before {
|
||||||
content: "\e946";
|
content: '\e946';
|
||||||
}
|
}
|
||||||
.icon-columndelete:before {
|
.icon-columndelete:before {
|
||||||
content: "\e947";
|
content: '\e947';
|
||||||
}
|
}
|
||||||
.icon-components:before {
|
.icon-components:before {
|
||||||
content: "\e949";
|
content: '\e949';
|
||||||
}
|
}
|
||||||
.icon-consignatarios:before {
|
.icon-consignatarios:before {
|
||||||
content: "\e94b";
|
content: '\e94b';
|
||||||
}
|
}
|
||||||
.icon-control:before {
|
.icon-control:before {
|
||||||
content: "\e94c";
|
content: '\e94c';
|
||||||
}
|
}
|
||||||
.icon-credit:before {
|
.icon-credit:before {
|
||||||
content: "\e94d";
|
content: '\e94d';
|
||||||
}
|
}
|
||||||
.icon-deaulter:before {
|
.icon-defaulter:before {
|
||||||
content: "\e94e";
|
content: '\e94e';
|
||||||
}
|
}
|
||||||
.icon-deletedTicket:before {
|
.icon-deletedTicket:before {
|
||||||
content: "\e94f";
|
content: '\e94f';
|
||||||
}
|
}
|
||||||
.icon-deleteline:before {
|
.icon-deleteline:before {
|
||||||
content: "\e950";
|
content: '\e950';
|
||||||
}
|
}
|
||||||
.icon-delivery:before {
|
.icon-delivery:before {
|
||||||
content: "\e951";
|
content: '\e951';
|
||||||
}
|
}
|
||||||
.icon-deliveryprices:before {
|
.icon-deliveryprices:before {
|
||||||
content: "\e952";
|
content: '\e952';
|
||||||
}
|
}
|
||||||
.icon-details:before {
|
.icon-details:before {
|
||||||
content: "\e954";
|
content: '\e954';
|
||||||
}
|
}
|
||||||
.icon-dfiscales:before {
|
.icon-dfiscales:before {
|
||||||
content: "\e955";
|
content: '\e955';
|
||||||
}
|
}
|
||||||
.icon-disabled:before {
|
.icon-disabled:before {
|
||||||
content: "\e965";
|
content: '\e965';
|
||||||
}
|
}
|
||||||
.icon-doc:before {
|
.icon-doc:before {
|
||||||
content: "\e956";
|
content: '\e956';
|
||||||
}
|
}
|
||||||
.icon-entry:before {
|
.icon-entry:before {
|
||||||
content: "\e958";
|
content: '\e958';
|
||||||
}
|
}
|
||||||
.icon-exit:before {
|
.icon-exit:before {
|
||||||
content: "\e959";
|
content: '\e959';
|
||||||
}
|
}
|
||||||
.icon-eye:before {
|
.icon-eye:before {
|
||||||
content: "\e95a";
|
content: '\e95a';
|
||||||
}
|
}
|
||||||
.icon-fixedPrice:before {
|
.icon-fixedPrice:before {
|
||||||
content: "\e95b";
|
content: '\e95b';
|
||||||
}
|
}
|
||||||
.icon-flower:before {
|
.icon-flower:before {
|
||||||
content: "\e95c";
|
content: '\e95c';
|
||||||
}
|
}
|
||||||
.icon-frozen:before {
|
.icon-frozen:before {
|
||||||
content: "\e95d";
|
content: '\e95d';
|
||||||
}
|
}
|
||||||
.icon-fruit:before {
|
.icon-fruit:before {
|
||||||
content: "\e95e";
|
content: '\e95e';
|
||||||
}
|
}
|
||||||
.icon-funeral:before {
|
.icon-funeral:before {
|
||||||
content: "\e95f";
|
content: '\e95f';
|
||||||
}
|
}
|
||||||
.icon-grafana:before {
|
.icon-grafana:before {
|
||||||
content: "\e931";
|
content: '\e931';
|
||||||
|
}
|
||||||
|
.icon-grafana:before {
|
||||||
|
content: '\e931';
|
||||||
}
|
}
|
||||||
.icon-greenery:before {
|
.icon-greenery:before {
|
||||||
content: "\e91e";
|
content: '\e91e';
|
||||||
}
|
}
|
||||||
.icon-greuge:before {
|
.icon-greuge:before {
|
||||||
content: "\e960";
|
content: '\e960';
|
||||||
}
|
}
|
||||||
.icon-grid:before {
|
.icon-grid:before {
|
||||||
content: "\e961";
|
content: '\e961';
|
||||||
}
|
}
|
||||||
.icon-handmade:before {
|
.icon-handmade:before {
|
||||||
content: "\e94a";
|
content: '\e94a';
|
||||||
}
|
}
|
||||||
.icon-handmadeArtificial:before {
|
.icon-handmadeArtificial:before {
|
||||||
content: "\e962";
|
content: '\e962';
|
||||||
}
|
}
|
||||||
.icon-headercol:before {
|
.icon-headercol:before {
|
||||||
content: "\e963";
|
content: '\e963';
|
||||||
}
|
}
|
||||||
.icon-info:before {
|
.icon-info:before {
|
||||||
content: "\e966";
|
content: '\e966';
|
||||||
}
|
}
|
||||||
.icon-inventory:before {
|
.icon-inventory:before {
|
||||||
content: "\e967";
|
content: '\e967';
|
||||||
}
|
}
|
||||||
.icon-invoice:before {
|
.icon-invoice:before {
|
||||||
content: "\e969";
|
content: '\e969';
|
||||||
}
|
}
|
||||||
.icon-invoice-in:before {
|
.icon-invoice-in:before {
|
||||||
content: "\e96a";
|
content: '\e96a';
|
||||||
}
|
}
|
||||||
.icon-invoice-in-create:before {
|
.icon-invoice-in-create:before {
|
||||||
content: "\e96b";
|
content: '\e96b';
|
||||||
}
|
}
|
||||||
.icon-invoice-out:before {
|
.icon-invoice-out:before {
|
||||||
content: "\e96c";
|
content: '\e96c';
|
||||||
}
|
}
|
||||||
.icon-isTooLittle:before {
|
.icon-isTooLittle:before {
|
||||||
content: "\e96e";
|
content: '\e96e';
|
||||||
}
|
}
|
||||||
.icon-item:before {
|
.icon-item:before {
|
||||||
content: "\e96f";
|
content: '\e96f';
|
||||||
}
|
}
|
||||||
.icon-languaje:before {
|
.icon-languaje:before {
|
||||||
content: "\e912";
|
content: '\e912';
|
||||||
}
|
}
|
||||||
.icon-lines:before {
|
.icon-lines:before {
|
||||||
content: "\e971";
|
content: '\e971';
|
||||||
}
|
}
|
||||||
.icon-linesprepaired:before {
|
.icon-linesprepaired:before {
|
||||||
content: "\e972";
|
content: '\e972';
|
||||||
}
|
}
|
||||||
.icon-link-to-corrected:before {
|
.icon-link-to-corrected:before {
|
||||||
content: "\e900";
|
content: '\e900';
|
||||||
}
|
}
|
||||||
.icon-link-to-correcting:before {
|
.icon-link-to-correcting:before {
|
||||||
content: "\e906";
|
content: '\e906';
|
||||||
}
|
}
|
||||||
.icon-logout:before {
|
.icon-logout:before {
|
||||||
content: "\e90a";
|
content: '\e90a';
|
||||||
}
|
}
|
||||||
.icon-mana:before {
|
.icon-mana:before {
|
||||||
content: "\e974";
|
content: '\e974';
|
||||||
}
|
}
|
||||||
.icon-mandatory:before {
|
.icon-mandatory:before {
|
||||||
content: "\e975";
|
content: '\e975';
|
||||||
}
|
}
|
||||||
.icon-net:before {
|
.icon-net:before {
|
||||||
content: "\e976";
|
content: '\e976';
|
||||||
}
|
}
|
||||||
.icon-newalbaran:before {
|
.icon-newalbaran:before {
|
||||||
content: "\e977";
|
content: '\e977';
|
||||||
}
|
}
|
||||||
.icon-niche:before {
|
.icon-niche:before {
|
||||||
content: "\e979";
|
content: '\e979';
|
||||||
}
|
}
|
||||||
.icon-no036:before {
|
.icon-no036:before {
|
||||||
content: "\e97a";
|
content: '\e97a';
|
||||||
}
|
}
|
||||||
.icon-noPayMethod:before {
|
.icon-noPayMethod:before {
|
||||||
content: "\e97b";
|
content: '\e97b';
|
||||||
}
|
}
|
||||||
.icon-notes:before {
|
.icon-notes:before {
|
||||||
content: "\e97c";
|
content: '\e97c';
|
||||||
}
|
}
|
||||||
.icon-noweb:before {
|
.icon-noweb:before {
|
||||||
content: "\e97e";
|
content: '\e97e';
|
||||||
}
|
}
|
||||||
.icon-onlinepayment:before {
|
.icon-onlinepayment:before {
|
||||||
content: "\e97f";
|
content: '\e97f';
|
||||||
}
|
}
|
||||||
.icon-package:before {
|
.icon-package:before {
|
||||||
content: "\e980";
|
content: '\e980';
|
||||||
}
|
}
|
||||||
.icon-payment:before {
|
.icon-payment:before {
|
||||||
content: "\e982";
|
content: '\e982';
|
||||||
}
|
}
|
||||||
.icon-pbx:before {
|
.icon-pbx:before {
|
||||||
content: "\e983";
|
content: '\e983';
|
||||||
}
|
}
|
||||||
.icon-pets:before {
|
.icon-pets:before {
|
||||||
content: "\e985";
|
content: '\e985';
|
||||||
}
|
}
|
||||||
.icon-photo:before {
|
.icon-photo:before {
|
||||||
content: "\e986";
|
content: '\e986';
|
||||||
}
|
}
|
||||||
.icon-plant:before {
|
.icon-plant:before {
|
||||||
content: "\e987";
|
content: '\e987';
|
||||||
}
|
}
|
||||||
.icon-polizon:before {
|
.icon-polizon:before {
|
||||||
content: "\e989";
|
content: '\e989';
|
||||||
}
|
}
|
||||||
.icon-preserved:before {
|
.icon-preserved:before {
|
||||||
content: "\e98a";
|
content: '\e98a';
|
||||||
}
|
}
|
||||||
.icon-recovery:before {
|
.icon-recovery:before {
|
||||||
content: "\e98b";
|
content: '\e98b';
|
||||||
}
|
}
|
||||||
.icon-regentry:before {
|
.icon-regentry:before {
|
||||||
content: "\e901";
|
content: '\e901';
|
||||||
}
|
}
|
||||||
.icon-reserva:before {
|
.icon-reserva:before {
|
||||||
content: "\e902";
|
content: '\e902';
|
||||||
}
|
}
|
||||||
.icon-revision:before {
|
.icon-revision:before {
|
||||||
content: "\e903";
|
content: '\e903';
|
||||||
}
|
}
|
||||||
.icon-risk:before {
|
.icon-risk:before {
|
||||||
content: "\e904";
|
content: '\e904';
|
||||||
}
|
}
|
||||||
.icon-services:before {
|
.icon-services:before {
|
||||||
content: "\e905";
|
content: '\e905';
|
||||||
}
|
}
|
||||||
.icon-settings:before {
|
.icon-settings:before {
|
||||||
content: "\e907";
|
content: '\e907';
|
||||||
}
|
}
|
||||||
.icon-shipment:before {
|
.icon-shipment:before {
|
||||||
content: "\e908";
|
content: '\e908';
|
||||||
}
|
}
|
||||||
.icon-sign:before {
|
.icon-sign:before {
|
||||||
content: "\e909";
|
content: '\e909';
|
||||||
}
|
}
|
||||||
.icon-sms:before {
|
.icon-sms:before {
|
||||||
content: "\e90b";
|
content: '\e90b';
|
||||||
}
|
}
|
||||||
.icon-solclaim:before {
|
.icon-solclaim:before {
|
||||||
content: "\e90c";
|
content: '\e90c';
|
||||||
}
|
}
|
||||||
.icon-solunion:before {
|
.icon-solunion:before {
|
||||||
content: "\e90d";
|
content: '\e90d';
|
||||||
}
|
}
|
||||||
.icon-splitline:before {
|
.icon-splitline:before {
|
||||||
content: "\e90e";
|
content: '\e90e';
|
||||||
}
|
}
|
||||||
.icon-splur:before {
|
.icon-splur:before {
|
||||||
content: "\e90f";
|
content: '\e90f';
|
||||||
}
|
}
|
||||||
.icon-stowaway:before {
|
.icon-stowaway:before {
|
||||||
content: "\e910";
|
content: '\e910';
|
||||||
}
|
}
|
||||||
.icon-supplier:before {
|
.icon-supplier:before {
|
||||||
content: "\e911";
|
content: '\e911';
|
||||||
}
|
}
|
||||||
.icon-supplierfalse:before {
|
.icon-supplierfalse:before {
|
||||||
content: "\e913";
|
content: '\e913';
|
||||||
}
|
}
|
||||||
.icon-tags:before {
|
.icon-tags:before {
|
||||||
content: "\e914";
|
content: '\e914';
|
||||||
}
|
}
|
||||||
.icon-tax:before {
|
.icon-tax:before {
|
||||||
content: "\e915";
|
content: '\e915';
|
||||||
}
|
}
|
||||||
.icon-thermometer:before {
|
.icon-thermometer:before {
|
||||||
content: "\e916";
|
content: '\e916';
|
||||||
}
|
}
|
||||||
.icon-ticket:before {
|
.icon-ticket:before {
|
||||||
content: "\e917";
|
content: '\e917';
|
||||||
}
|
}
|
||||||
.icon-ticketAdd:before {
|
.icon-ticketAdd:before {
|
||||||
content: "\e918";
|
content: '\e918';
|
||||||
}
|
}
|
||||||
.icon-traceability:before {
|
.icon-traceability:before {
|
||||||
content: "\e919";
|
content: '\e919';
|
||||||
}
|
}
|
||||||
.icon-transaction:before {
|
.icon-transaction:before {
|
||||||
content: "\e93b";
|
content: '\e93b';
|
||||||
|
}
|
||||||
|
.icon-transaction:before {
|
||||||
|
content: '\e93b';
|
||||||
}
|
}
|
||||||
.icon-treatments:before {
|
.icon-treatments:before {
|
||||||
content: "\e91c";
|
content: '\e91c';
|
||||||
}
|
}
|
||||||
.icon-trolley:before {
|
.icon-trolley:before {
|
||||||
content: "\e91a";
|
content: '\e91a';
|
||||||
}
|
}
|
||||||
.icon-troncales:before {
|
.icon-troncales:before {
|
||||||
content: "\e91b";
|
content: '\e91b';
|
||||||
}
|
}
|
||||||
.icon-unavailable:before {
|
.icon-unavailable:before {
|
||||||
content: "\e91d";
|
content: '\e91d';
|
||||||
}
|
}
|
||||||
.icon-volume:before {
|
.icon-volume:before {
|
||||||
content: "\e91f";
|
content: '\e91f';
|
||||||
}
|
}
|
||||||
.icon-wand:before {
|
.icon-wand:before {
|
||||||
content: "\e920";
|
content: '\e920';
|
||||||
}
|
}
|
||||||
.icon-web:before {
|
.icon-web:before {
|
||||||
content: "\e921";
|
content: '\e921';
|
||||||
}
|
}
|
||||||
.icon-wiki:before {
|
.icon-wiki:before {
|
||||||
content: "\e922";
|
content: '\e922';
|
||||||
}
|
}
|
||||||
.icon-worker:before {
|
.icon-worker:before {
|
||||||
content: "\e923";
|
content: '\e923';
|
||||||
}
|
}
|
||||||
.icon-zone:before {
|
.icon-zone:before {
|
||||||
content: "\e924";
|
content: '\e924';
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ $warning: #f4b974;
|
||||||
$success: $positive;
|
$success: $positive;
|
||||||
$alert: $negative;
|
$alert: $negative;
|
||||||
$white: #fff;
|
$white: #fff;
|
||||||
$dark: #3c3b3b;
|
$dark: #3d3d3d;
|
||||||
// custom
|
// custom
|
||||||
$color-link: #66bfff;
|
$color-link: #66bfff;
|
||||||
$color-spacer-light: #a3a3a31f;
|
$color-spacer-light: #a3a3a31f;
|
||||||
|
|
|
@ -26,11 +26,11 @@ export function isValidDate(date) {
|
||||||
* // returns "02/12/2022"
|
* // returns "02/12/2022"
|
||||||
* toDateFormat(new Date(2022, 11, 2));
|
* toDateFormat(new Date(2022, 11, 2));
|
||||||
*/
|
*/
|
||||||
export function toDateFormat(date) {
|
export function toDateFormat(date, locale = 'es-ES') {
|
||||||
if (!isValidDate(date)) {
|
if (!isValidDate(date)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return new Date(date).toLocaleDateString('es-ES', {
|
return new Date(date).toLocaleDateString(locale, {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: '2-digit',
|
month: '2-digit',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
|
@ -56,7 +56,7 @@ export function toTimeFormat(date, showSeconds = false) {
|
||||||
if (!isValidDate(date)) {
|
if (!isValidDate(date)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return new Date(date).toLocaleDateString('es-ES', {
|
return new Date(date).toLocaleTimeString('es-ES', {
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
second: showSeconds ? '2-digit' : undefined,
|
second: showSeconds ? '2-digit' : undefined,
|
||||||
|
@ -91,3 +91,42 @@ export function toDateTimeFormat(date, showSeconds = false) {
|
||||||
second: showSeconds ? '2-digit' : undefined,
|
second: showSeconds ? '2-digit' : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts seconds to a formatted string representing hours and minutes (hh:mm).
|
||||||
|
* @param {number} seconds - The number of seconds to convert.
|
||||||
|
* @param {boolean} includeHSuffix - Optional parameter indicating whether to include "h." after the hour.
|
||||||
|
* @returns {string} A string representing the time in the format "hh:mm" with optional "h." suffix.
|
||||||
|
*/
|
||||||
|
export function secondsToHoursMinutes(seconds, includeHSuffix = true) {
|
||||||
|
if (!seconds) return includeHSuffix ? '00:00 h.' : '00:00';
|
||||||
|
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
const remainingMinutes = seconds % 3600;
|
||||||
|
const minutes = Math.floor(remainingMinutes / 60);
|
||||||
|
const formattedHours = hours < 10 ? '0' + hours : hours;
|
||||||
|
const formattedMinutes = minutes < 10 ? '0' + minutes : minutes;
|
||||||
|
|
||||||
|
// Append "h." if includeHSuffix is true
|
||||||
|
const suffix = includeHSuffix ? ' h.' : '';
|
||||||
|
// Return formatted string
|
||||||
|
return formattedHours + ':' + formattedMinutes + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTimeDifferenceWithToday(date) {
|
||||||
|
let today = Date.vnNew();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
date = new Date(date);
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
return today - date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLower(date) {
|
||||||
|
return getTimeDifferenceWithToday(date) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBigger(date) {
|
||||||
|
return getTimeDifferenceWithToday(date) < 0;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import toLowerCase from './toLowerCase';
|
import toLowerCase from './toLowerCase';
|
||||||
import toDate from './toDate';
|
import toDate from './toDate';
|
||||||
import toDateString from './toDateString';
|
import toDateString from './toDateString';
|
||||||
import toDateHour from './toDateHour';
|
import toDateHourMin from './toDateHourMin';
|
||||||
|
import toDateHourMinSec from './toDateHourMinSec';
|
||||||
import toRelativeDate from './toRelativeDate';
|
import toRelativeDate from './toRelativeDate';
|
||||||
import toCurrency from './toCurrency';
|
import toCurrency from './toCurrency';
|
||||||
import toPercentage from './toPercentage';
|
import toPercentage from './toPercentage';
|
||||||
|
@ -16,7 +17,8 @@ export {
|
||||||
toDate,
|
toDate,
|
||||||
toHour,
|
toHour,
|
||||||
toDateString,
|
toDateString,
|
||||||
toDateHour,
|
toDateHourMin,
|
||||||
|
toDateHourMinSec,
|
||||||
toRelativeDate,
|
toRelativeDate,
|
||||||
toCurrency,
|
toCurrency,
|
||||||
toPercentage,
|
toPercentage,
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
export default function toDateHourMin(date) {
|
||||||
|
const dateHour = new Date(date).toLocaleDateString('es-ES', {
|
||||||
|
timeZone: 'Europe/Madrid',
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
});
|
||||||
|
return dateHour;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export default function toDateHour(date) {
|
export default function toDateHourMinSec(date) {
|
||||||
const dateHour = new Date(date).toLocaleDateString('es-ES', {
|
const dateHour = new Date(date).toLocaleDateString('es-ES', {
|
||||||
timeZone: 'Europe/Madrid',
|
timeZone: 'Europe/Madrid',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
1227
src/i18n/en/index.js
1227
src/i18n/en/index.js
File diff suppressed because it is too large
Load Diff
1227
src/i18n/es/index.js
1227
src/i18n/es/index.js
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,28 @@
|
||||||
import en from './en';
|
const files = import.meta.glob(`./locale/*.yml`);
|
||||||
import es from './es';
|
const modules = import.meta.glob(`../pages/**/locale/*.yml`);
|
||||||
export const localeEquivalence = {
|
|
||||||
'en':'en-GB'
|
const translations = {};
|
||||||
|
|
||||||
|
for (const file in files) {
|
||||||
|
const lang = file.split('/').at(2).split('.')[0];
|
||||||
|
|
||||||
|
files[file]()
|
||||||
|
.then((g) => {
|
||||||
|
translations[lang] = g.default;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
const actualLang = lang + '.yml';
|
||||||
|
for (const module in modules) {
|
||||||
|
if (!module.endsWith(actualLang)) continue;
|
||||||
|
modules[module]().then((t) => {
|
||||||
|
Object.assign(translations[lang], t.default);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
export default {
|
});
|
||||||
en: en,
|
}
|
||||||
es: es,
|
|
||||||
|
export const localeEquivalence = {
|
||||||
|
en: 'en-GB',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default translations;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,79 @@
|
||||||
|
<script setup>
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
function navigate(id) {
|
||||||
|
router.push({ path: `/agency/${id}` });
|
||||||
|
}
|
||||||
|
function exprBuilder(param, value) {
|
||||||
|
if (!value) return;
|
||||||
|
if (param !== 'search') return;
|
||||||
|
|
||||||
|
if (!isNaN(value)) return { id: value };
|
||||||
|
|
||||||
|
return { name: { like: `%${value}%` } };
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
:info="t('You can search by name')"
|
||||||
|
:label="t('Search agency')"
|
||||||
|
data-key="AgencyList"
|
||||||
|
url="Agencies"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<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>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
isOwn: Tiene propietario
|
||||||
|
isAnyVolumeAllowed: Permite cualquier volumen
|
||||||
|
Search agency: Buscar agencia
|
||||||
|
You can search by name: Puedes buscar por nombre
|
||||||
|
en:
|
||||||
|
isOwn: Has owner
|
||||||
|
isAnyVolumeAllowed: Allows any volume
|
||||||
|
</i18n>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const routeId = route.params?.id || null;
|
||||||
|
const warehouses = ref([]);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="warehouses"
|
||||||
|
sort-by="name"
|
||||||
|
@on-fetch="(data) => (warehouses = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModel :url="`Agencies/${routeId}`" model="agency" auto-load>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow>
|
||||||
|
<VnInput v-model="data.name" :label="t('globals.name')" />
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
v-model="data.warehouseFk"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
:label="t('globals.warehouse')"
|
||||||
|
:options="warehouses"
|
||||||
|
use-input
|
||||||
|
input-debounce="0"
|
||||||
|
:is-clearable="false"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<QCheckbox :label="t('agency.isOwn')" v-model="data.isOwn" />
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('agency.isAnyVolumeAllowed')"
|
||||||
|
v-model="data.isAnyVolumeAllowed"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, watch } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
|
||||||
|
import AgencyDescriptor from 'pages/Agency/Card/AgencyDescriptor.vue';
|
||||||
|
import VnCard from 'components/common/VnCard.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const arrayData = useArrayData('Agency', {
|
||||||
|
url: `Agencies/${route.params.id}`,
|
||||||
|
});
|
||||||
|
const { store } = arrayData;
|
||||||
|
onMounted(async () => await arrayData.fetch({ append: false }));
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
async (newId) => {
|
||||||
|
if (newId) {
|
||||||
|
store.url = `Agencies/${newId}`;
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnCard
|
||||||
|
data-key="Agency"
|
||||||
|
base-url="Agencies"
|
||||||
|
:descriptor="AgencyDescriptor"
|
||||||
|
searchbar-data-key="AgencyList"
|
||||||
|
searchbar-url="Agencies"
|
||||||
|
searchbar-label="agency.searchBar.label"
|
||||||
|
searchbar-info="agency.searchBar.info"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { params } = useRoute();
|
||||||
|
const entityId = computed(() => props.id || params.id);
|
||||||
|
const { store } = useArrayData('Parking');
|
||||||
|
const card = computed(() => store.data);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<CardDescriptor
|
||||||
|
module="Agency"
|
||||||
|
data-key="Agency"
|
||||||
|
:url="`Agencies/${entityId}`"
|
||||||
|
:title="card?.name"
|
||||||
|
:subtitle="props.id"
|
||||||
|
>
|
||||||
|
<template #body="{ entity: agency }">
|
||||||
|
<VnLv :label="t('globals.name')" :value="agency.name" />
|
||||||
|
</template>
|
||||||
|
</CardDescriptor>
|
||||||
|
</template>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<script setup>
|
||||||
|
import VnLog from 'src/components/common/VnLog.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnLog model="Agency" url="/AgencyLogs" />
|
||||||
|
</template>
|
|
@ -0,0 +1,60 @@
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const routeId = route.params.id;
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="vn-card-list">
|
||||||
|
<VnPaginate
|
||||||
|
data-key="AgencyModes"
|
||||||
|
:url="`AgencyModes`"
|
||||||
|
:filter="{ where: { agencyFk: routeId } }"
|
||||||
|
order="name"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<CardList
|
||||||
|
:id="row.id"
|
||||||
|
:key="row.id"
|
||||||
|
:title="row.name"
|
||||||
|
v-for="row of rows"
|
||||||
|
>
|
||||||
|
<template #list-items>
|
||||||
|
<VnLv
|
||||||
|
:label="t('globals.description')"
|
||||||
|
:value="row?.description"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('deliveryMethod')"
|
||||||
|
:value="row?.deliveryMethodFk"
|
||||||
|
/>
|
||||||
|
<VnLv label="m3" :value="row?.m3" />
|
||||||
|
<VnLv :label="t('inflation')" :value="row?.inflation" />
|
||||||
|
<VnLv :label="t('globals.code')" :value="row?.code" />
|
||||||
|
</template>
|
||||||
|
</CardList>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
isOwn: Tiene propietario
|
||||||
|
isAnyVolumeAllowed: Permite cualquier volumen
|
||||||
|
Search agency: Buscar agencia
|
||||||
|
You can search by name: Puedes buscar por nombre
|
||||||
|
deliveryMethod: Método de entrega
|
||||||
|
inflation: Inflación
|
||||||
|
en:
|
||||||
|
isOwn: Has owner
|
||||||
|
isAnyVolumeAllowed: Allows any volume
|
||||||
|
</i18n>
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { params } = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const entityId = computed(() => $props.id || params.id);
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: ['id', 'sectorFk', 'code', 'pickingOrder', 'row', 'column'],
|
||||||
|
include: [{ relation: 'sector', scope: { fields: ['id', 'description'] } }],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<CardSummary :url="`Agencies/${entityId}`">
|
||||||
|
<template #header="{ entity: agency }">{{ agency.name }}</template>
|
||||||
|
<template #body="{ entity: agency }">
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<QCardSection class="q-pa-none">
|
||||||
|
<VnTitle
|
||||||
|
:url="`#/agency/${entityId}/basic-data`"
|
||||||
|
:text="t('globals.pageTitles.basicData')"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<VnLv :label="t('globals.name')" :value="agency.name" />
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('agency.isOwn')"
|
||||||
|
v-model="agency.isOwn"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('agency.isAnyVolumeAllowed')"
|
||||||
|
v-model="agency.isAnyVolumeAllowed"
|
||||||
|
:disable="true"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CardSummary>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,136 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
import FormModelPopup from 'src/components/FormModelPopup.vue';
|
||||||
|
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const paginate = ref();
|
||||||
|
const dialog = ref();
|
||||||
|
const routeId = computed(() => route.params.id);
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const initialData = computed(() => {
|
||||||
|
return {
|
||||||
|
agencyFk: routeId.value,
|
||||||
|
workCenterFk: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
async function deleteWorCenter(id) {
|
||||||
|
try {
|
||||||
|
await axios.delete(`AgencyWorkCenters/${id}`).then(async () => {
|
||||||
|
quasar.notify({
|
||||||
|
message: t('agency.notification.removeItem'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
quasar.notify({
|
||||||
|
message: t('agency.notification.removeItemError'),
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
paginate.value.fetch();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="centerCard">
|
||||||
|
<FetchData
|
||||||
|
url="workCenters"
|
||||||
|
sort-by="name"
|
||||||
|
@on-fetch="(data) => (warehouses = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnPaginate
|
||||||
|
ref="paginate"
|
||||||
|
data-key="AgencyWorkCenters"
|
||||||
|
url="AgencyWorkCenters"
|
||||||
|
:filter="{ where: { agencyFk: routeId } }"
|
||||||
|
order="id"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QCard
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
:key="row.id"
|
||||||
|
v-for="row of rows"
|
||||||
|
class="card q-pa-md q-mb-sm"
|
||||||
|
>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection side-left>
|
||||||
|
<VnLv :value="row?.workCenter?.name" icon="delete" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QBtn
|
||||||
|
@click.stop="deleteWorCenter(row.id)"
|
||||||
|
icon="delete"
|
||||||
|
color="primary"
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QPageSticky :offset="[18, 18]">
|
||||||
|
<QBtn @click.stop="dialog.show()" color="primary" fab icon="add">
|
||||||
|
<QDialog ref="dialog">
|
||||||
|
<FormModelPopup
|
||||||
|
:title="t('Add work center')"
|
||||||
|
url-create="AgencyWorkCenters"
|
||||||
|
model="AgencyWorkCenter"
|
||||||
|
:form-initial-data="initialData"
|
||||||
|
@on-data-saved="paginate.fetch()"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<VnSelect
|
||||||
|
v-model="data.workCenterFk"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
:label="t('workCenter')"
|
||||||
|
:options="warehouses"
|
||||||
|
use-input
|
||||||
|
input-debounce="0"
|
||||||
|
:is-clearable="false"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</QDialog>
|
||||||
|
</QBtn>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('globals.new') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.centerCard {
|
||||||
|
padding: 5%;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 50%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
workCenter removed successfully: Centro de trabajo eliminado correctamente
|
||||||
|
This workCenter is already assigned to this agency: Este workCenter ya está asignado a esta agencia
|
||||||
|
Add work center: Añadir centro de trabajo
|
||||||
|
workCenter: Centro de trabajo
|
||||||
|
</i18n>
|
|
@ -0,0 +1,11 @@
|
||||||
|
agency:
|
||||||
|
isOwn: Own
|
||||||
|
isAnyVolumeAllowed: Any volume allowed
|
||||||
|
notification:
|
||||||
|
removeItemError: Error removing agency
|
||||||
|
removeItem: WorkCenter removed successfully
|
||||||
|
pageTitles:
|
||||||
|
agency: Agency
|
||||||
|
searchBar:
|
||||||
|
info: You can search by agency code
|
||||||
|
label: Search agency...
|
|
@ -0,0 +1,12 @@
|
||||||
|
agency:
|
||||||
|
isOwn: Propio
|
||||||
|
isAnyVolumeAllowed: Cualquier volumen
|
||||||
|
removeItem: Agencia eliminada correctamente
|
||||||
|
notification:
|
||||||
|
removeItemError: Error al eliminar la agencia
|
||||||
|
removeItem: Centro de trabajo eliminado correctamente
|
||||||
|
pageTitles:
|
||||||
|
agency: Agencia
|
||||||
|
searchBar:
|
||||||
|
info: Puedes buscar por nombre o id
|
||||||
|
label: Buscar agencia...
|
|
@ -2,22 +2,21 @@
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useStateStore } from 'src/stores/useStateStore';
|
import { useStateStore } from 'src/stores/useStateStore';
|
||||||
import { toDate, toPercentage, toCurrency } from 'filters/index';
|
import { toDate, toPercentage, toCurrency } from 'filters/index';
|
||||||
import { tMobile } from 'src/composables/tMobile';
|
import { tMobile } from 'src/composables/tMobile';
|
||||||
import CrudModel from 'src/components/CrudModel.vue';
|
import CrudModel from 'src/components/CrudModel.vue';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
|
||||||
const stateStore = computed(() => useStateStore());
|
const stateStore = computed(() => useStateStore());
|
||||||
const claim = ref(null);
|
const claim = ref(null);
|
||||||
const claimRef = ref();
|
const claimRef = ref();
|
||||||
|
@ -38,10 +37,11 @@ const marker_labels = [
|
||||||
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
|
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
|
||||||
];
|
];
|
||||||
const multiplicatorValue = ref();
|
const multiplicatorValue = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'Id',
|
name: 'id',
|
||||||
label: t('Id item'),
|
label: t('Id item'),
|
||||||
field: (row) => row.itemFk,
|
field: (row) => row.itemFk,
|
||||||
},
|
},
|
||||||
|
@ -119,7 +119,7 @@ async function updateDestinations(claimDestinationFk) {
|
||||||
|
|
||||||
async function updateDestination(claimDestinationFk, row, options = {}) {
|
async function updateDestination(claimDestinationFk, row, options = {}) {
|
||||||
if (claimDestinationFk) {
|
if (claimDestinationFk) {
|
||||||
await axios.post('Claims/updateClaimDestination', {
|
await post('Claims/updateClaimDestination', {
|
||||||
claimDestinationFk,
|
claimDestinationFk,
|
||||||
rows: Array.isArray(row) ? row : [row],
|
rows: Array.isArray(row) ? row : [row],
|
||||||
});
|
});
|
||||||
|
@ -128,7 +128,7 @@ async function updateDestination(claimDestinationFk, row, options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function regularizeClaim() {
|
async function regularizeClaim() {
|
||||||
await axios.post(`Claims/${claimId}/regularizeClaim`);
|
await post(`Claims/${claimId}/regularizeClaim`);
|
||||||
await claimRef.value.fetch();
|
await claimRef.value.fetch();
|
||||||
await arrayData.fetch({ append: false });
|
await arrayData.fetch({ append: false });
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
|
@ -147,7 +147,7 @@ async function onUpdateGreugeAccept() {
|
||||||
const freightPickUpPrice =
|
const freightPickUpPrice =
|
||||||
(await axios.get(`GreugeConfigs/findOne`)).data.freightPickUpPrice *
|
(await axios.get(`GreugeConfigs/findOne`)).data.freightPickUpPrice *
|
||||||
multiplicatorValue.value;
|
multiplicatorValue.value;
|
||||||
await axios.post(`Greuges`, {
|
await post(`Greuges`, {
|
||||||
clientFk: claim.value.clientFk,
|
clientFk: claim.value.clientFk,
|
||||||
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
|
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
|
||||||
amount: freightPickUpPrice,
|
amount: freightPickUpPrice,
|
||||||
|
@ -166,14 +166,22 @@ async function save(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importToNewRefundTicket() {
|
async function importToNewRefundTicket() {
|
||||||
const query = `ClaimBeginnings/${claimId}/importToNewRefundTicket`;
|
await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`);
|
||||||
await axios.post(query);
|
await claimActionsForm.value.reload();
|
||||||
claimActionsForm.value.reload();
|
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: t('globals.dataSaved'),
|
message: t('globals.dataSaved'),
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function post(query, params) {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
await axios.post(query, params);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
@ -280,6 +288,7 @@ async function importToNewRefundTicket() {
|
||||||
:default-save="false"
|
:default-save="false"
|
||||||
:default-reset="false"
|
:default-reset="false"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
|
:limit="0"
|
||||||
auto-load
|
auto-load
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
|
@ -291,9 +300,15 @@ async function importToNewRefundTicket() {
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
v-model:selected="selectedRows"
|
v-model:selected="selectedRows"
|
||||||
:grid="$q.screen.lt.md"
|
:grid="$q.screen.lt.md"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
|
||||||
:hide-bottom="true"
|
|
||||||
>
|
>
|
||||||
|
<template #body-cell-id="{ value }">
|
||||||
|
<QTd align="center">
|
||||||
|
<span class="link">
|
||||||
|
{{ value }}
|
||||||
|
<ItemDescriptorProxy :id="value" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
<template #body-cell-ticket="{ value }">
|
<template #body-cell-ticket="{ value }">
|
||||||
<QTd align="center">
|
<QTd align="center">
|
||||||
<span class="link">
|
<span class="link">
|
||||||
|
@ -304,7 +319,7 @@ async function importToNewRefundTicket() {
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-destination="{ row }">
|
<template #body-cell-destination="{ row }">
|
||||||
<QTd>
|
<QTd>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
v-model="row.claimDestinationFk"
|
v-model="row.claimDestinationFk"
|
||||||
:options="destinationTypes"
|
:options="destinationTypes"
|
||||||
option-label="description"
|
option-label="description"
|
||||||
|
@ -346,7 +361,7 @@ async function importToNewRefundTicket() {
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection side>
|
<QItemSection side>
|
||||||
<QItemLabel v-if="column.name === 'destination'">
|
<QItemLabel v-if="column.name === 'destination'">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
v-model="props.row.claimDestinationFk"
|
v-model="props.row.claimDestinationFk"
|
||||||
:options="destinationTypes"
|
:options="destinationTypes"
|
||||||
option-label="description"
|
option-label="description"
|
||||||
|
@ -385,6 +400,7 @@ async function importToNewRefundTicket() {
|
||||||
icon="check"
|
icon="check"
|
||||||
@click="regularizeClaim"
|
@click="regularizeClaim"
|
||||||
:disable="claim.claimStateFk == resolvedStateId"
|
:disable="claim.claimStateFk == resolvedStateId"
|
||||||
|
:loading="loading"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<QBtn
|
<QBtn
|
||||||
|
@ -396,6 +412,7 @@ async function importToNewRefundTicket() {
|
||||||
:title="t('Change destination')"
|
:title="t('Change destination')"
|
||||||
icon="swap_horiz"
|
icon="swap_horiz"
|
||||||
@click="dialogDestination = !dialogDestination"
|
@click="dialogDestination = !dialogDestination"
|
||||||
|
:loading="loading"
|
||||||
/>
|
/>
|
||||||
<QBtn
|
<QBtn
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -406,6 +423,7 @@ async function importToNewRefundTicket() {
|
||||||
icon="Upload"
|
icon="Upload"
|
||||||
@click="importToNewRefundTicket"
|
@click="importToNewRefundTicket"
|
||||||
:disable="claim.claimStateFk == resolvedStateId"
|
:disable="claim.claimStateFk == resolvedStateId"
|
||||||
|
:loading="loading"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</CrudModel>
|
</CrudModel>
|
||||||
|
@ -420,7 +438,7 @@ async function importToNewRefundTicket() {
|
||||||
</QItem>
|
</QItem>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
class="q-pa-sm"
|
class="q-pa-sm"
|
||||||
v-model="claimDestinationFk"
|
v-model="claimDestinationFk"
|
||||||
:options="destinationTypes"
|
:options="destinationTypes"
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -24,7 +25,7 @@ const claimFilter = {
|
||||||
'workerFk',
|
'workerFk',
|
||||||
'claimStateFk',
|
'claimStateFk',
|
||||||
'packages',
|
'packages',
|
||||||
'hasToPickUp',
|
'pickup',
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -36,39 +37,31 @@ const claimFilter = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const workers = ref([]);
|
|
||||||
const workersCopy = ref([]);
|
|
||||||
const claimStates = ref([]);
|
const claimStates = ref([]);
|
||||||
const claimStatesCopy = ref([]);
|
const claimStatesCopy = ref([]);
|
||||||
|
const optionsList = ref([]);
|
||||||
|
|
||||||
function setWorkers(data) {
|
const workersOptions = ref([]);
|
||||||
workers.value = data;
|
|
||||||
workersCopy.value = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setClaimStates(data) {
|
function setClaimStates(data) {
|
||||||
claimStates.value = data;
|
claimStates.value = data;
|
||||||
claimStatesCopy.value = data;
|
claimStatesCopy.value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workerFilter = {
|
async function getEnumValues() {
|
||||||
options: workers,
|
optionsList.value = [{ id: null, description: t('claim.basicData.null') }];
|
||||||
filterFn: (options, value) => {
|
const { data } = await axios.get(`Applications/get-enum-values`, {
|
||||||
const search = value.toLowerCase();
|
params: {
|
||||||
|
schema: 'vn',
|
||||||
if (value === '') return workersCopy.value;
|
table: 'claim',
|
||||||
|
column: 'pickup',
|
||||||
return options.value.filter((row) => {
|
|
||||||
const id = row.id;
|
|
||||||
const name = row.name.toLowerCase();
|
|
||||||
|
|
||||||
const idMatches = id == search;
|
|
||||||
const nameMatches = name.indexOf(search) > -1;
|
|
||||||
|
|
||||||
return idMatches || nameMatches;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
for (let value of data)
|
||||||
|
optionsList.value.push({ id: value, description: t(`claim.basicData.${value}`) });
|
||||||
|
}
|
||||||
|
|
||||||
|
getEnumValues();
|
||||||
|
|
||||||
const statesFilter = {
|
const statesFilter = {
|
||||||
options: claimStates,
|
options: claimStates,
|
||||||
|
@ -89,7 +82,7 @@ const statesFilter = {
|
||||||
<FetchData
|
<FetchData
|
||||||
url="Workers/activeWithInheritedRole"
|
url="Workers/activeWithInheritedRole"
|
||||||
:filter="{ where: { role: 'salesPerson' } }"
|
:filter="{ where: { role: 'salesPerson' } }"
|
||||||
@on-fetch="setWorkers"
|
@on-fetch="(data) => (workersOptions = data)"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
|
<FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
|
||||||
|
@ -118,18 +111,15 @@ const statesFilter = {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<QSelect
|
<VnSelect
|
||||||
|
:label="t('claim.basicData.assignedTo')"
|
||||||
v-model="data.workerFk"
|
v-model="data.workerFk"
|
||||||
:options="workers"
|
:options="workersOptions"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
emit-value
|
emit-value
|
||||||
:label="t('claim.basicData.assignedTo')"
|
auto-load
|
||||||
map-options
|
|
||||||
use-input
|
|
||||||
@filter="(value, update) => filter(value, update, workerFilter)"
|
|
||||||
:rules="validate('claim.claimStateFk')"
|
:rules="validate('claim.claimStateFk')"
|
||||||
:input-debounce="0"
|
|
||||||
>
|
>
|
||||||
<template #before>
|
<template #before>
|
||||||
<QAvatar color="orange">
|
<QAvatar color="orange">
|
||||||
|
@ -140,7 +130,7 @@ const statesFilter = {
|
||||||
/>
|
/>
|
||||||
</QAvatar>
|
</QAvatar>
|
||||||
</template>
|
</template>
|
||||||
</QSelect>
|
</VnSelect>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<QSelect
|
<QSelect
|
||||||
|
@ -168,13 +158,19 @@ const statesFilter = {
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<QCheckbox
|
<QSelect
|
||||||
v-model="data.hasToPickUp"
|
v-model="data.pickup"
|
||||||
:label="t('claim.basicData.picked')"
|
:options="optionsList"
|
||||||
/>
|
option-value="id"
|
||||||
|
option-label="description"
|
||||||
|
emit-value
|
||||||
|
:label="t('claim.basicData.pickup')"
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
</QSelect>
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,46 +1,15 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import VnCard from 'components/common/VnCard.vue';
|
||||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import ClaimDescriptor from './ClaimDescriptor.vue';
|
import ClaimDescriptor from './ClaimDescriptor.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
|
||||||
import useCardSize from 'src/composables/useCardSize';
|
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
|
||||||
const { t } = useI18n();
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
<VnCard
|
||||||
<VnSearchbar
|
data-key="Claim"
|
||||||
data-key="ClaimList"
|
base-url="Claims"
|
||||||
url="Claims/filter"
|
:descriptor="ClaimDescriptor"
|
||||||
:label="t('Search claim')"
|
searchbar-data-key="ClaimList"
|
||||||
:info="t('You can search by claim id or customer name')"
|
searchbar-url="Claims/filter"
|
||||||
|
searchbar-label="Search claim"
|
||||||
|
searchbar-info="You can search by claim id or customer name"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
|
||||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
|
||||||
<QScrollArea class="fit">
|
|
||||||
<ClaimDescriptor />
|
|
||||||
<QSeparator />
|
|
||||||
<LeftMenu source="card" />
|
|
||||||
</QScrollArea>
|
|
||||||
</QDrawer>
|
|
||||||
<QPageContainer>
|
|
||||||
<QPage>
|
|
||||||
<VnSubToolbar />
|
|
||||||
<div :class="useCardSize()">
|
|
||||||
<RouterView></RouterView>
|
|
||||||
</div>
|
|
||||||
</QPage>
|
|
||||||
</QPageContainer>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
|
||||||
es:
|
|
||||||
Search claim: Buscar reclamación
|
|
||||||
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
|
|
||||||
Details: Detalles
|
|
||||||
Notes: Notas
|
|
||||||
Action: Acción
|
|
||||||
</i18n>
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||||
import { getUrl } from 'src/composables/getUrl';
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -73,8 +74,9 @@ const filter = {
|
||||||
|
|
||||||
const STATE_COLOR = {
|
const STATE_COLOR = {
|
||||||
pending: 'warning',
|
pending: 'warning',
|
||||||
managed: 'info',
|
incomplete: 'info',
|
||||||
resolved: 'positive',
|
resolved: 'positive',
|
||||||
|
canceled: 'negative',
|
||||||
};
|
};
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
return STATE_COLOR[code];
|
return STATE_COLOR[code];
|
||||||
|
@ -127,17 +129,24 @@ onMounted(async () => {
|
||||||
</VnLv>
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
v-if="entity.worker"
|
v-if="entity.worker"
|
||||||
:label="t('claim.card.assignedTo')"
|
:label="t('claim.card.attendedBy')"
|
||||||
:value="entity.worker.user.name"
|
:value="entity.worker.user.name"
|
||||||
>
|
>
|
||||||
<template #value>
|
<template #value>
|
||||||
<VnUserLink
|
<VnUserLink
|
||||||
:name="entity.worker.user.name"
|
:name="entity.worker.user.nickname"
|
||||||
:worker-id="entity.worker.id"
|
:worker-id="entity.worker.id"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
<VnLv :label="t('claim.card.zone')" :value="entity.ticket?.zone?.name" />
|
<VnLv :label="t('claim.card.zone')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ entity.ticket?.zone?.name }}
|
||||||
|
<ZoneDescriptorProxy :id="entity.ticket?.zone?.id" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('claim.card.province')"
|
:label="t('claim.card.province')"
|
||||||
:value="entity.ticket?.address?.province?.name"
|
:value="entity.ticket?.address?.province?.name"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import CrudModel from 'components/CrudModel.vue';
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import { tMobile } from 'composables/tMobile';
|
import { tMobile } from 'composables/tMobile';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -150,10 +150,8 @@ const columns = computed(() => [
|
||||||
<QTable
|
<QTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
|
||||||
row-key="$index"
|
row-key="$index"
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
hide-pagination
|
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
:grid="$q.screen.lt.md"
|
:grid="$q.screen.lt.md"
|
||||||
table-header-class="text-left"
|
table-header-class="text-left"
|
||||||
|
@ -163,7 +161,7 @@ const columns = computed(() => [
|
||||||
auto-width
|
auto-width
|
||||||
@keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
|
@keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
|
||||||
>
|
>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
v-model="row[col.model]"
|
v-model="row[col.model]"
|
||||||
:options="col.options"
|
:options="col.options"
|
||||||
:option-value="col.optionValue"
|
:option-value="col.optionValue"
|
||||||
|
@ -183,7 +181,7 @@ const columns = computed(() => [
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
</template>
|
</template>
|
||||||
</VnSelectFilter>
|
</VnSelect>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #item="props">
|
<template #item="props">
|
||||||
|
@ -200,7 +198,7 @@ const columns = computed(() => [
|
||||||
<QList dense>
|
<QList dense>
|
||||||
<QItem v-for="col in props.cols" :key="col.name">
|
<QItem v-for="col in props.cols" :key="col.name">
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="col.label"
|
:label="col.label"
|
||||||
v-model="props.row[col.model]"
|
v-model="props.row[col.model]"
|
||||||
:options="col.options"
|
:options="col.options"
|
||||||
|
|
|
@ -45,20 +45,25 @@ async function onFetchClaim(data) {
|
||||||
|
|
||||||
const amount = ref();
|
const amount = ref();
|
||||||
const amountClaimed = ref();
|
const amountClaimed = ref();
|
||||||
async function onFetch(rows) {
|
async function onFetch(rows, newRows) {
|
||||||
|
if (newRows) rows = newRows;
|
||||||
amount.value = 0;
|
amount.value = 0;
|
||||||
amountClaimed.value = 0;
|
amountClaimed.value = 0;
|
||||||
if (!rows || !rows.length) return;
|
if (!rows || !rows.length) return;
|
||||||
|
|
||||||
amount.value = rows.reduce(
|
for (const row of rows) {
|
||||||
(accumulator, { sale }) => accumulator + sale.price * sale.quantity,
|
const { sale } = row;
|
||||||
0
|
amount.value = amount.value + totalRow(sale);
|
||||||
);
|
const price = row.quantity * sale.price;
|
||||||
|
const discount = (sale.discount * price) / 100;
|
||||||
|
amountClaimed.value = amountClaimed.value + (price - discount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
amountClaimed.value = rows.reduce(
|
function totalRow({ price, quantity, discount }) {
|
||||||
(accumulator, line) => accumulator + line.sale.price * line.quantity,
|
const amount = price * quantity;
|
||||||
0
|
const appliedDiscount = (discount * amount) / 100;
|
||||||
);
|
return amount - appliedDiscount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
|
@ -102,12 +107,7 @@ const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'total',
|
name: 'total',
|
||||||
label: t('Total'),
|
label: t('Total'),
|
||||||
field: ({ sale }) => {
|
field: ({ sale }) => totalRow(sale),
|
||||||
const amount = sale.price * sale.quantity;
|
|
||||||
const appliedDiscount = (sale.discount * amount) / 100;
|
|
||||||
|
|
||||||
return amount - appliedDiscount;
|
|
||||||
},
|
|
||||||
format: (value) => toCurrency(value),
|
format: (value) => toCurrency(value),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
|
@ -121,11 +121,6 @@ async function fetchMana() {
|
||||||
mana.value = response.data;
|
mana.value = response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateQuantity({ id, quantity }) {
|
|
||||||
if (!id) return;
|
|
||||||
await axios.patch(`ClaimBeginnings/${id}`, { quantity });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateDiscount({ saleFk, discount, canceller }) {
|
async function updateDiscount({ saleFk, discount, canceller }) {
|
||||||
const body = { salesIds: [saleFk], newDiscount: discount };
|
const body = { salesIds: [saleFk], newDiscount: discount };
|
||||||
const claimId = claim.value.ticketFk;
|
const claimId = claim.value.ticketFk;
|
||||||
|
@ -134,6 +129,7 @@ async function updateDiscount({ saleFk, discount, canceller }) {
|
||||||
await axios.post(query, body, {
|
await axios.post(query, body, {
|
||||||
signal: canceller.signal,
|
signal: canceller.signal,
|
||||||
});
|
});
|
||||||
|
await claimLinesForm.value.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUpdateDiscount(response) {
|
function onUpdateDiscount(response) {
|
||||||
|
@ -155,6 +151,13 @@ function showImportDialog() {
|
||||||
})
|
})
|
||||||
.onOk(() => claimLinesForm.value.reload());
|
.onOk(() => claimLinesForm.value.reload());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function saveWhenHasChanges() {
|
||||||
|
if (claimLinesForm.value.getChanges().updates) {
|
||||||
|
await claimLinesForm.value.onSubmit();
|
||||||
|
await claimLinesForm.value.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
||||||
|
@ -181,8 +184,7 @@ function showImportDialog() {
|
||||||
@on-fetch="onFetchClaim"
|
@on-fetch="onFetchClaim"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<div class="column items-center">
|
<div class="q-pa-md">
|
||||||
<div class="list">
|
|
||||||
<CrudModel
|
<CrudModel
|
||||||
data-key="ClaimLines"
|
data-key="ClaimLines"
|
||||||
ref="claimLinesForm"
|
ref="claimLinesForm"
|
||||||
|
@ -190,44 +192,31 @@ function showImportDialog() {
|
||||||
save-url="ClaimBeginnings/crud"
|
save-url="ClaimBeginnings/crud"
|
||||||
:filter="linesFilter"
|
:filter="linesFilter"
|
||||||
@on-fetch="onFetch"
|
@on-fetch="onFetch"
|
||||||
@save-changes="onFetch"
|
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
:default-save="false"
|
:default-save="false"
|
||||||
:default-reset="false"
|
:default-reset="false"
|
||||||
auto-load
|
auto-load
|
||||||
|
:limit="0"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<QTable
|
<QTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
:dense="$q.screen.lt.md"
|
:dense="$q.screen.lt.md"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
|
||||||
row-key="id"
|
row-key="id"
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
hide-pagination
|
|
||||||
:grid="$q.screen.lt.md"
|
:grid="$q.screen.lt.md"
|
||||||
>
|
>
|
||||||
<template #body-cell-claimed="{ row, value }">
|
<template #body-cell-claimed="{ row }">
|
||||||
<QTd auto-width align="right" class="text-primary">
|
<QTd auto-width align="right" class="text-primary">
|
||||||
<span>{{ value }}</span>
|
|
||||||
|
|
||||||
<QPopupEdit
|
|
||||||
v-model="row.quantity"
|
|
||||||
v-slot="scope"
|
|
||||||
:title="t('Claimed quantity')"
|
|
||||||
@update:model-value="updateQuantity(row)"
|
|
||||||
buttons
|
|
||||||
>
|
|
||||||
<QInput
|
<QInput
|
||||||
v-model="scope.value"
|
v-model="row.quantity"
|
||||||
type="number"
|
type="number"
|
||||||
dense
|
dense
|
||||||
autofocus
|
@keyup.enter="saveWhenHasChanges()"
|
||||||
@keyup.enter="scope.set"
|
@blur="saveWhenHasChanges()"
|
||||||
@focus="($event) => $event.target.select()"
|
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-description="{ row, value }">
|
<template #body-cell-description="{ row, value }">
|
||||||
|
@ -274,32 +263,18 @@ function showImportDialog() {
|
||||||
</QItemLabel>
|
</QItemLabel>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection side>
|
<QItemSection side>
|
||||||
<template
|
<template v-if="column.name === 'claimed'">
|
||||||
v-if="column.name === 'claimed'"
|
|
||||||
>
|
|
||||||
<QItemLabel class="text-primary">
|
<QItemLabel class="text-primary">
|
||||||
{{ column.value }}
|
|
||||||
<QPopupEdit
|
|
||||||
v-model="props.row.quantity"
|
|
||||||
v-slot="scope"
|
|
||||||
:title="t('Claimed quantity')"
|
|
||||||
@update:model-value="
|
|
||||||
updateQuantity(props.row)
|
|
||||||
"
|
|
||||||
buttons
|
|
||||||
>
|
|
||||||
<QInput
|
<QInput
|
||||||
v-model="scope.value"
|
v-model="props.row.quantity"
|
||||||
type="number"
|
type="number"
|
||||||
dense
|
dense
|
||||||
autofocus
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
@keyup.enter="
|
||||||
@focus="
|
saveWhenHasChanges()
|
||||||
($event) =>
|
|
||||||
$event.target.select()
|
|
||||||
"
|
"
|
||||||
|
@blur="saveWhenHasChanges()"
|
||||||
/>
|
/>
|
||||||
</QPopupEdit>
|
|
||||||
</QItemLabel>
|
</QItemLabel>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
|
@ -338,7 +313,6 @@ function showImportDialog() {
|
||||||
</template>
|
</template>
|
||||||
</CrudModel>
|
</CrudModel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
<QBtn fab color="primary" icon="add" @click="showImportDialog()" />
|
<QBtn fab color="primary" icon="add" @click="showImportDialog()" />
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import { toDate, toCurrency, toPercentage } from 'filters/index';
|
import { toDate, toCurrency, toPercentage } from 'filters/index';
|
||||||
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
defineEmits([...useDialogPluginComponent.emits]);
|
defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
@ -34,6 +35,7 @@ const columns = computed(() => [
|
||||||
label: t('Quantity'),
|
label: t('Quantity'),
|
||||||
field: (row) => row.quantity,
|
field: (row) => row.quantity,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
default: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
@ -74,7 +76,6 @@ async function importLines() {
|
||||||
const body = sales.map((row) => ({
|
const body = sales.map((row) => ({
|
||||||
claimFk: route.params.id,
|
claimFk: route.params.id,
|
||||||
saleFk: row.saleFk,
|
saleFk: row.saleFk,
|
||||||
quantity: row.quantity,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
canceller = new AbortController();
|
canceller = new AbortController();
|
||||||
|
@ -118,16 +119,21 @@ function cancel() {
|
||||||
<QBtn icon="close" flat round dense v-close-popup />
|
<QBtn icon="close" flat round dense v-close-popup />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QTable
|
<QTable
|
||||||
class="my-sticky-header-table"
|
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:rows="claimableSales"
|
:rows="claimableSales"
|
||||||
:pagination="{ rowsPerPage: 10 }"
|
|
||||||
row-key="saleFk"
|
row-key="saleFk"
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
v-model:selected="selected"
|
v-model:selected="selected"
|
||||||
square
|
square
|
||||||
flat
|
flat
|
||||||
/>
|
>
|
||||||
|
<template #body-cell-description="{ row, value }">
|
||||||
|
<QTd auto-width align="right" class="link">
|
||||||
|
{{ value }}
|
||||||
|
<ItemDescriptorProxy :id="row.itemFk"></ItemDescriptorProxy>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
<QCardActions align="right">
|
<QCardActions align="right">
|
||||||
<QBtn :label="t('globals.cancel')" color="primary" flat @click="cancel" />
|
<QBtn :label="t('globals.cancel')" color="primary" flat @click="cancel" />
|
||||||
|
@ -149,33 +155,6 @@ function cancel() {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.my-sticky-header-table {
|
|
||||||
height: 400px;
|
|
||||||
|
|
||||||
thead tr th {
|
|
||||||
position: sticky;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead tr:first-child th {
|
|
||||||
/* this is when the loading indicator appears */
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.q-table--loading thead tr:last-child th {
|
|
||||||
/* height of all previous header rows */
|
|
||||||
top: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /* prevent scrolling behind sticky top row on focus */
|
|
||||||
tbody {
|
|
||||||
/* height of all previous header rows */
|
|
||||||
scroll-margin-top: 48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Available sales lines: Líneas de venta disponibles
|
Available sales lines: Líneas de venta disponibles
|
||||||
|
|
|
@ -16,7 +16,7 @@ const claimId = computed(() => $props.id || route.params.id);
|
||||||
|
|
||||||
const claimFilter = {
|
const claimFilter = {
|
||||||
where: { claimFk: claimId.value },
|
where: { claimFk: claimId.value },
|
||||||
fields: ['created', 'workerFk', 'text'],
|
fields: ['id', 'created', 'workerFk', 'text'],
|
||||||
include: {
|
include: {
|
||||||
relation: 'worker',
|
relation: 'worker',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -38,10 +38,11 @@ const body = {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VnNotes
|
<VnNotes
|
||||||
style="overflow-y: auto"
|
|
||||||
:add-note="$props.addNote"
|
|
||||||
url="claimObservations"
|
url="claimObservations"
|
||||||
|
:add-note="$props.addNote"
|
||||||
:filter="claimFilter"
|
:filter="claimFilter"
|
||||||
:body="body"
|
:body="body"
|
||||||
|
v-bind="$attrs"
|
||||||
|
style="overflow-y: auto"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { onMounted, ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toDate, toCurrency } from 'src/filters';
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
@ -12,8 +12,12 @@ import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
|
||||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||||
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||||
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import dashIfEmpty from 'src/filters/dashIfEmpty';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getTokenMultimedia } = useSession();
|
const { getTokenMultimedia } = useSession();
|
||||||
const token = getTokenMultimedia();
|
const token = getTokenMultimedia();
|
||||||
|
@ -26,7 +30,7 @@ const $props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const entityId = computed(() => $props.id || route.params.id);
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const ClaimStates = ref([]);
|
||||||
const claimUrl = ref();
|
const claimUrl = ref();
|
||||||
const salixUrl = ref();
|
const salixUrl = ref();
|
||||||
const claimDmsRef = ref();
|
const claimDmsRef = ref();
|
||||||
|
@ -98,8 +102,9 @@ const detailsColumns = ref([
|
||||||
|
|
||||||
const STATE_COLOR = {
|
const STATE_COLOR = {
|
||||||
pending: 'warning',
|
pending: 'warning',
|
||||||
managed: 'info',
|
incomplete: 'info',
|
||||||
resolved: 'positive',
|
resolved: 'positive',
|
||||||
|
canceled: 'negative',
|
||||||
};
|
};
|
||||||
function stateColor(code) {
|
function stateColor(code) {
|
||||||
return STATE_COLOR[code];
|
return STATE_COLOR[code];
|
||||||
|
@ -161,6 +166,10 @@ function openDialog(dmsId) {
|
||||||
multimediaSlide.value = dmsId;
|
multimediaSlide.value = dmsId;
|
||||||
multimediaDialog.value = true;
|
multimediaDialog.value = true;
|
||||||
}
|
}
|
||||||
|
async function changeState(value) {
|
||||||
|
await axios.patch(`Claims/updateClaim/${entityId.value}`, { claimStateFk: value });
|
||||||
|
router.go(route.fullPath);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -170,6 +179,7 @@ function openDialog(dmsId) {
|
||||||
@on-fetch="(data) => setClaimDms(data)"
|
@on-fetch="(data) => setClaimDms(data)"
|
||||||
ref="claimDmsRef"
|
ref="claimDmsRef"
|
||||||
/>
|
/>
|
||||||
|
<FetchData url="ClaimStates" @on-fetch="(data) => (ClaimStates = data)" auto-load />
|
||||||
<CardSummary
|
<CardSummary
|
||||||
ref="summary"
|
ref="summary"
|
||||||
:url="`Claims/${entityId}/getSummary`"
|
:url="`Claims/${entityId}/getSummary`"
|
||||||
|
@ -177,7 +187,37 @@ function openDialog(dmsId) {
|
||||||
@on-fetch="getClaimDms"
|
@on-fetch="getClaimDms"
|
||||||
>
|
>
|
||||||
<template #header="{ entity: { claim } }">
|
<template #header="{ entity: { claim } }">
|
||||||
{{ claim.id }} - {{ claim.client.name }}
|
{{ claim.id }} - {{ claim.client.name }} ({{ claim.client.id }})
|
||||||
|
</template>
|
||||||
|
<template #header-right>
|
||||||
|
<QBtnDropdown
|
||||||
|
side
|
||||||
|
top
|
||||||
|
color="black"
|
||||||
|
text-color="white"
|
||||||
|
:label="t('ticket.summary.changeState')"
|
||||||
|
>
|
||||||
|
<QList>
|
||||||
|
<QVirtualScroll
|
||||||
|
style="max-height: 300px"
|
||||||
|
:items="ClaimStates"
|
||||||
|
separator
|
||||||
|
v-slot="{ item, index }"
|
||||||
|
>
|
||||||
|
<QItem
|
||||||
|
:key="index"
|
||||||
|
dense
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="changeState(item.id)"
|
||||||
|
>
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ item.description }}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QVirtualScroll>
|
||||||
|
</QList>
|
||||||
|
</QBtnDropdown>
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ entity: { claim, salesClaimed, developments } }">
|
<template #body="{ entity: { claim, salesClaimed, developments } }">
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
|
@ -214,16 +254,15 @@ function openDialog(dmsId) {
|
||||||
</VnLv>
|
</VnLv>
|
||||||
<VnLv :label="t('claim.summary.customer')">
|
<VnLv :label="t('claim.summary.customer')">
|
||||||
<template #value>
|
<template #value>
|
||||||
<VnUserLink
|
<span class="link cursor-pointer">
|
||||||
:name="claim.client?.name"
|
{{ claim.client?.name }}
|
||||||
:worker-id="claim.client?.id"
|
<CustomerDescriptorProxy :id="claim.clientFk" />
|
||||||
/>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</VnLv>
|
</VnLv>
|
||||||
<QCheckbox
|
<VnLv
|
||||||
:label="t('claim.basicData.picked')"
|
:label="t('claim.basicData.pickup')"
|
||||||
v-model="claim.hasToPickUp"
|
:value="`${dashIfEmpty(claim.pickup)}`"
|
||||||
:disable="true"
|
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-three">
|
<QCard class="vn-three">
|
||||||
|
@ -280,6 +319,48 @@ function openDialog(dmsId) {
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
<QCard class="vn-two" v-if="claimDms.length > 0">
|
||||||
|
<VnTitle
|
||||||
|
:url="`#/claim/${entityId}/photos`"
|
||||||
|
:text="t('claim.summary.photos')"
|
||||||
|
/>
|
||||||
|
<div class="container">
|
||||||
|
<div
|
||||||
|
class="multimedia-container"
|
||||||
|
v-for="(media, index) of claimDms"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="relative-position">
|
||||||
|
<QIcon
|
||||||
|
name="play_circle"
|
||||||
|
color="primary"
|
||||||
|
size="xl"
|
||||||
|
class="absolute-center zindex"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click.stop="openDialog(media.dmsFk)"
|
||||||
|
>
|
||||||
|
<QTooltip>Video</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QCard class="multimedia relative-position">
|
||||||
|
<QImg
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
>
|
||||||
|
</QImg>
|
||||||
|
<video
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
muted="muted"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
<QCard class="vn-two" v-if="developments.length > 0">
|
<QCard class="vn-two" v-if="developments.length > 0">
|
||||||
<VnTitle
|
<VnTitle
|
||||||
:url="claimUrl + 'development'"
|
:url="claimUrl + 'development'"
|
||||||
|
@ -302,49 +383,6 @@ function openDialog(dmsId) {
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-max" v-if="claimDms.length > 0">
|
|
||||||
<VnTitle
|
|
||||||
:url="`#/claim/${entityId}/photos`"
|
|
||||||
:text="t('claim.summary.photos')"
|
|
||||||
/>
|
|
||||||
<div class="container">
|
|
||||||
<div
|
|
||||||
class="multimedia-container"
|
|
||||||
v-for="(media, index) of claimDms"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<div class="relative-position">
|
|
||||||
<QIcon
|
|
||||||
name="play_circle"
|
|
||||||
color="primary"
|
|
||||||
size="xl"
|
|
||||||
class="absolute-center zindex"
|
|
||||||
v-if="media.isVideo"
|
|
||||||
@click.stop="openDialog(media.dmsFk)"
|
|
||||||
>
|
|
||||||
<QTooltip>Video</QTooltip>header
|
|
||||||
</QIcon>
|
|
||||||
<QCard class="multimedia relative-position">
|
|
||||||
<QImg
|
|
||||||
:src="media.url"
|
|
||||||
class="rounded-borders cursor-pointer fit"
|
|
||||||
@click="openDialog(media.dmsFk)"
|
|
||||||
v-if="!media.isVideo"
|
|
||||||
>
|
|
||||||
</QImg>
|
|
||||||
<video
|
|
||||||
:src="media.url"
|
|
||||||
class="rounded-borders cursor-pointer fit"
|
|
||||||
muted="muted"
|
|
||||||
v-if="media.isVideo"
|
|
||||||
@click="openDialog(media.dmsFk)"
|
|
||||||
/>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</QCard>
|
|
||||||
|
|
||||||
<QCard class="vn-max">
|
<QCard class="vn-max">
|
||||||
<VnTitle :url="claimUrl + 'action'" :text="t('claim.summary.actions')" />
|
<VnTitle :url="claimUrl + 'action'" :text="t('claim.summary.actions')" />
|
||||||
<div id="slider-container" class="q-px-xl q-py-md">
|
<div id="slider-container" class="q-px-xl q-py-md">
|
||||||
|
@ -448,4 +486,8 @@ function openDialog(dmsId) {
|
||||||
.zindex {
|
.zindex {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.change-state {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Salesperson')"
|
:label="t('Salesperson')"
|
||||||
v-model="params.salesPersonFk"
|
v-model="params.salesPersonFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -87,7 +87,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Attender')"
|
:label="t('Attender')"
|
||||||
v-model="params.attenderFk"
|
v-model="params.attenderFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -110,7 +110,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('Responsible')"
|
:label="t('Responsible')"
|
||||||
v-model="params.claimResponsibleFk"
|
v-model="params.claimResponsibleFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -133,7 +133,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="states">
|
<QItemSection v-if="states">
|
||||||
<VnSelectFilter
|
<VnSelect
|
||||||
:label="t('State')"
|
:label="t('State')"
|
||||||
v-model="params.claimStateFk"
|
v-model="params.claimStateFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -149,6 +149,15 @@ const states = ref();
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<QCheckbox
|
||||||
|
v-model="params.myTeam"
|
||||||
|
:label="t('myTeam')"
|
||||||
|
toggle-indeterminate
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
<QExpansionItem :label="t('More options')" expand-separator>
|
<QExpansionItem :label="t('More options')" expand-separator>
|
||||||
<!-- <QItem>
|
<!-- <QItem>
|
||||||
|
@ -192,6 +201,7 @@ en:
|
||||||
claimResponsibleFk: Responsible
|
claimResponsibleFk: Responsible
|
||||||
claimStateFk: State
|
claimStateFk: State
|
||||||
created: Created
|
created: Created
|
||||||
|
myTeam: My team
|
||||||
es:
|
es:
|
||||||
params:
|
params:
|
||||||
search: Contiene
|
search: Contiene
|
||||||
|
@ -211,4 +221,5 @@ es:
|
||||||
Item: Artículo
|
Item: Artículo
|
||||||
Created: Creada
|
Created: Creada
|
||||||
More options: Más opciones
|
More options: Más opciones
|
||||||
|
myTeam: Mi equipo
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue