diff --git a/src/boot/axios.js b/src/boot/axios.js index 9b32275bd..99a163cca 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -5,8 +5,10 @@ import useNotify from 'src/composables/useNotify.js'; const session = useSession(); const { notify } = useNotify(); +const baseUrl = '/api/'; -axios.defaults.baseURL = '/api/'; +axios.defaults.baseURL = baseUrl; +const axiosNoError = axios.create({ baseURL: baseUrl }); const onRequest = (config) => { const token = session.getToken(); @@ -79,5 +81,7 @@ const onResponseError = (error) => { axios.interceptors.request.use(onRequest, onRequestError); axios.interceptors.response.use(onResponse, onResponseError); +axiosNoError.interceptors.request.use(onRequest); +axiosNoError.interceptors.response.use(onResponse); -export { onRequest, onResponseError }; +export { onRequest, onResponseError, axiosNoError }; diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 570d0cbfe..26160929c 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -67,9 +67,13 @@ const mixinRules = [ requiredFieldRule, ...($attrs.rules ?? []), (val) => { - const { min } = vnInputRef.value.$attrs; + const { min, max } = vnInputRef.value.$attrs; if (!min) return null; if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min }); + if (!max) return null; + if (max > 0) { + if (Math.floor(val) > max) return t('inputMax', { value: max }); + } }, ]; </script> @@ -116,8 +120,10 @@ const mixinRules = [ <i18n> en: inputMin: Must be more than {value} + inputMax: Must be less than {value} es: inputMin: Debe ser mayor a {value} + inputMax: Debe ser menor a {value} </i18n> <style lang="scss"> .q-field__append { diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue index 96e47d6d7..fd8993d6f 100644 --- a/src/components/common/VnInputDate.vue +++ b/src/components/common/VnInputDate.vue @@ -2,6 +2,7 @@ import { onMounted, watch, computed, ref } from 'vue'; import { date } from 'quasar'; import { useI18n } from 'vue-i18n'; +import { useAttrs } from 'vue'; const model = defineModel({ type: [String, Date] }); const $props = defineProps({ @@ -14,29 +15,19 @@ const $props = defineProps({ default: true, }, }); +import { useValidator } from 'src/composables/useValidator'; +const { validations } = useValidator(); const { t } = useI18n(); -const requiredFieldRule = (val) => !!val || t('globals.fieldRequired'); +const requiredFieldRule = (val) => validations().required($attrs.required, val); const dateFormat = 'DD/MM/YYYY'; const isPopupOpen = ref(); const hover = ref(); const mask = ref(); +const $attrs = useAttrs(); -onMounted(() => { - // fix quasar bug - mask.value = '##/##/####'; -}); - -const styleAttrs = computed(() => { - return $props.isOutlined - ? { - dense: true, - outlined: true, - rounded: true, - } - : {}; -}); +const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const formattedDate = computed({ get() { @@ -48,15 +39,12 @@ const formattedDate = computed({ let newDate; if (value) { // parse input - if (value.includes('/')) { - if (value.length == 6) value = value + new Date().getFullYear(); - if (value.length >= 10) { - if (value.at(2) == '/') value = value.split('/').reverse().join('/'); - value = date.formatDate( - new Date(value).toISOString(), - 'YYYY-MM-DDTHH:mm:ss.SSSZ' - ); - } + if (value.includes('/') && value.length >= 10) { + if (value.at(2) == '/') value = value.split('/').reverse().join('/'); + value = date.formatDate( + new Date(value).toISOString(), + 'YYYY-MM-DDTHH:mm:ss.SSSZ' + ); } const [year, month, day] = value.split('-').map((e) => parseInt(e)); newDate = new Date(year, month - 1, day); @@ -79,12 +67,25 @@ const formattedDate = computed({ const popupDate = computed(() => model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value ); - +onMounted(() => { + // fix quasar bug + mask.value = '##/##/####'; +}); watch( () => model.value, (val) => (formattedDate.value = val), { immediate: true } ); + +const styleAttrs = computed(() => { + return $props.isOutlined + ? { + dense: true, + outlined: true, + rounded: true, + } + : {}; +}); </script> <template> @@ -96,9 +97,10 @@ watch( placeholder="dd/mm/aaaa" v-bind="{ ...$attrs, ...styleAttrs }" :class="{ required: $attrs.required }" - :rules="$attrs.required ? [requiredFieldRule] : null" + :rules="mixinRules" :clearable="false" @click="isPopupOpen = true" + hide-bottom-space > <template #append> <QIcon diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue index b3478bb23..42ec79479 100644 --- a/src/components/common/VnInputTime.vue +++ b/src/components/common/VnInputTime.vue @@ -1,8 +1,10 @@ <script setup> -import { computed, ref } from 'vue'; +import { computed, ref, useAttrs } from 'vue'; import { useI18n } from 'vue-i18n'; import { date } from 'quasar'; - +import { useValidator } from 'src/composables/useValidator'; +const { validations } = useValidator(); +const $attrs = useAttrs(); const model = defineModel({ type: String }); const props = defineProps({ timeOnly: { @@ -16,8 +18,8 @@ const props = defineProps({ }); const initialDate = ref(model.value ?? Date.vnNew()); const { t } = useI18n(); -const requiredFieldRule = (val) => !!val || t('globals.fieldRequired'); - +const requiredFieldRule = (val) => validations().required($attrs.required, val); +const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const dateFormat = 'HH:mm'; const isPopupOpen = ref(); const hover = ref(); @@ -74,9 +76,10 @@ function dateToTime(newDate) { v-bind="{ ...$attrs, ...styleAttrs }" :class="{ required: $attrs.required }" style="min-width: 100px" - :rules="$attrs.required ? [requiredFieldRule] : null" + :rules="mixinRules" @click="isPopupOpen = false" type="time" + hide-bottom-space > <template #append> <QIcon diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue index 8256ec5b0..b6cbfbafd 100644 --- a/src/components/common/VnLocation.vue +++ b/src/components/common/VnLocation.vue @@ -2,21 +2,37 @@ import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue'; import VnSelectDialog from 'components/common/VnSelectDialog.vue'; import { useI18n } from 'vue-i18n'; - +import { ref } from 'vue'; const { t } = useI18n(); -const value = defineModel({ type: [String, Number, Object] }); +const emit = defineEmits(['update:model-value', 'update:options']); +const props = defineProps({ + location: { + type: Object, + default: null, + }, +}); +const modelValue = ref( + props.location + ? `${props.location?.postcode} - ${props.location?.city}(${props.location?.province?.name}), ${props.location?.country?.name}` + : null +); function showLabel(data) { return `${data.code} - ${data.town}(${data.province}), ${data.country}`; } +const handleModelValue = (data) => { + emit('update:model-value', data); +}; </script> <template> <VnSelectDialog - v-model="value" - option-value="code" + v-model="modelValue" option-filter-value="search" - :option-label="(opt) => showLabel(opt)" + :option-label=" + (opt) => (typeof modelValue === 'string' ? modelValue : showLabel(opt)) + " url="Postcodes/filter" + @update:model-value="handleModelValue" :use-like="false" :label="t('Location')" :placeholder="t('search_by_postalcode')" @@ -27,7 +43,14 @@ function showLabel(data) { :emit-value="false" > <template #form> - <CreateNewPostcode @on-data-saved="(newValue) => (value = newValue)" /> + <CreateNewPostcode + @on-data-saved=" + (newValue) => { + modelValue = newValue; + emit('update:model-value', newValue); + } + " + /> </template> <template #option="{ itemProps, opt }"> <QItem v-bind="itemProps"> diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnSectionMain.vue index 0c1641ce1..9975b1011 100644 --- a/src/components/common/VnSectionMain.vue +++ b/src/components/common/VnSectionMain.vue @@ -20,6 +20,8 @@ onMounted(() => (stateStore.leftDrawer = $props.leftDrawer)); </QScrollArea> </QDrawer> <QPageContainer> - <RouterView></RouterView> + <QPage> + <RouterView /> + </QPage> </QPageContainer> </template> diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index 1e3a32f48..ed842103f 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -1,7 +1,8 @@ <script setup> -import { ref, toRefs, computed, watch, onMounted } from 'vue'; +import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'src/components/FetchData.vue'; +import { useValidator } from 'src/composables/useValidator'; const emit = defineEmits(['update:modelValue', 'update:options', 'remove']); const $props = defineProps({ @@ -82,10 +83,11 @@ const $props = defineProps({ default: false, }, }); - +const { validations } = useValidator(); +const requiredFieldRule = (val) => validations().required($attrs.required, val); +const $attrs = useAttrs(); const { t } = useI18n(); -const requiredFieldRule = (val) => val ?? t('globals.fieldRequired'); - +const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } = toRefs($props); const myOptions = ref([]); @@ -248,8 +250,9 @@ const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val); ref="vnSelectRef" lazy-rules :class="{ required: $attrs.required }" - :rules="$attrs.required ? [requiredFieldRule] : null" + :rules="mixinRules" virtual-scroll-slice-size="options.length" + hide-bottom-space > <template v-if="isClearable" #append> <QIcon diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 47a29fc91..0b7c08e2b 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -105,6 +105,7 @@ globals: weight: Weight pageTitles: logIn: Login + addressEdit: Update address summary: Summary basicData: Basic data log: Logs diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 61db6b381..be657c792 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -107,6 +107,7 @@ globals: weight: Peso pageTitles: logIn: Inicio de sesión + addressEdit: Modificar consignatario summary: Resumen basicData: Datos básicos log: Historial diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue index d76707079..166c33e1a 100644 --- a/src/pages/Customer/Card/CustomerAddress.vue +++ b/src/pages/Customer/Card/CustomerAddress.vue @@ -5,15 +5,12 @@ import { useRoute, useRouter } from 'vue-router'; import axios from 'axios'; -import FetchData from 'components/FetchData.vue'; - const { t } = useI18n(); const route = useRoute(); const router = useRouter(); const addresses = ref([]); const client = ref(null); -const provincesLocation = ref([]); const addressFilter = { fields: [ @@ -41,7 +38,13 @@ const addressFilter = { { relation: 'province', scope: { - fields: ['id', 'name'], + fields: ['id', 'name', 'countryFk'], + include: [ + { + relation: 'country', + scope: { fields: ['id', 'name'] }, + }, + ], }, }, ], @@ -83,13 +86,6 @@ const getClientData = async (id) => { } }; -const setProvince = (provinceFk) => { - const result = provincesLocation.value.filter( - (province) => province.id === provinceFk - ); - return result[0]?.name || ''; -}; - const isDefaultAddress = (address) => { return client?.value?.defaultAddressFk === address.id ? 1 : 0; }; @@ -128,12 +124,6 @@ const toCustomerAddressEdit = (addressId) => { </script> <template> - <FetchData - @on-fetch="(data) => (provincesLocation = data)" - auto-load - url="Provinces/location" - /> - <div class="full-width flex justify-center"> <QCard class="card-width q-pa-lg" v-if="addresses.length"> <QCardSection> @@ -177,7 +167,7 @@ const toCustomerAddressEdit = (addressId) => { <div>{{ item.street }}</div> <div> {{ item.postalCode }} - {{ item.city }}, - {{ setProvince(item.provinceFk) }} + {{ item.province.name }} </div> <div> {{ item.phone }} diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue index c2adace5e..d8c07a46f 100644 --- a/src/pages/Customer/Card/CustomerFiscalData.vue +++ b/src/pages/Customer/Card/CustomerFiscalData.vue @@ -94,7 +94,7 @@ function handleLocation(data, location) { <VnLocation :rules="validate('Worker.postcode')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" - v-model="data.postcode" + :location="data" @update:model-value="(location) => handleLocation(data, location)" /> </VnRow> diff --git a/src/pages/Customer/components/CustomerAddressEdit.vue b/src/pages/Customer/components/CustomerAddressEdit.vue index 0aa46f85d..2252a96dc 100644 --- a/src/pages/Customer/components/CustomerAddressEdit.vue +++ b/src/pages/Customer/components/CustomerAddressEdit.vue @@ -177,7 +177,12 @@ function handleLocation(data, location) { <VnLocation :rules="validate('Worker.postcode')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" - v-model="data.postalCode" + :location="{ + postcode: data.postalCode, + city: data.city, + province: data.province, + country: data.province.country, + }" @update:model-value="(location) => handleLocation(data, location)" ></VnLocation> </div> diff --git a/src/pages/Supplier/Card/SupplierAddresses.vue b/src/pages/Supplier/Card/SupplierAddresses.vue index 4bb6d5622..f46a3be19 100644 --- a/src/pages/Supplier/Card/SupplierAddresses.vue +++ b/src/pages/Supplier/Card/SupplierAddresses.vue @@ -26,7 +26,13 @@ const addressesFilter = { { relation: 'province', scope: { - fields: ['id', 'name'], + fields: ['id', 'name', 'countryFk'], + include: [ + { + relation: 'country', + scope: { fields: ['id', 'name'] }, + }, + ], }, }, ], diff --git a/src/pages/Supplier/Card/SupplierAddressesCreate.vue b/src/pages/Supplier/Card/SupplierAddressesCreate.vue index 6e51ee94e..290373039 100644 --- a/src/pages/Supplier/Card/SupplierAddressesCreate.vue +++ b/src/pages/Supplier/Card/SupplierAddressesCreate.vue @@ -21,6 +21,7 @@ const newAddressForm = reactive({ provinceFk: null, phone: null, mobile: null, + province: null, }); const onDataSaved = () => { @@ -84,7 +85,16 @@ function handleLocation(data, location) { <VnLocation :rules="validate('Worker.postcode')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" - v-model="data.location" + :location=" + data.postalCode + ? { + postcode: data.postalCode, + city: data.city, + province: data.province, + country: data.province.country, + } + : null + " @update:model-value="(location) => handleLocation(data, location)" > </VnLocation> diff --git a/src/pages/Supplier/Card/SupplierFiscalData.vue b/src/pages/Supplier/Card/SupplierFiscalData.vue index 65c34c4ed..60cd6770b 100644 --- a/src/pages/Supplier/Card/SupplierFiscalData.vue +++ b/src/pages/Supplier/Card/SupplierFiscalData.vue @@ -19,8 +19,8 @@ const sageTransactionTypesOptions = ref([]); const supplierActivitiesOptions = ref([]); function handleLocation(data, location) { - const { town, code, provinceFk, countryFk } = location ?? {}; - data.postCode = code; + const { town, label, provinceFk, countryFk } = location ?? {}; + data.postCode = label; data.city = town; data.provinceFk = provinceFk; data.countryFk = countryFk; @@ -51,6 +51,23 @@ function handleLocation(data, location) { :url="`Suppliers/${route.params.id}`" :url-update="`Suppliers/${route.params.id}/updateFiscalData`" model="supplier" + :filter="{ + fields: ['id', 'name', 'city', 'postCode', 'countryFk', 'provinceFk'], + include: [ + { + relation: 'province', + scope: { + fields: ['id', 'name'], + }, + }, + { + relation: 'country', + scope: { + fields: ['id', 'name'], + }, + }, + ], + }" auto-load :clear-store-on-unmount="false" > @@ -130,7 +147,12 @@ function handleLocation(data, location) { <VnLocation :rules="validate('Worker.postcode')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" - v-model="data.postCode" + :location="{ + postcode: data.postCode, + city: data.city, + province: data.province, + country: data.country, + }" @update:model-value="(location) => handleLocation(data, location)" > </VnLocation> diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue index 7615dea02..2f0d55fbe 100644 --- a/src/pages/Wagon/Type/WagonTypeList.vue +++ b/src/pages/Wagon/Type/WagonTypeList.vue @@ -2,14 +2,13 @@ import { ref, computed } from 'vue'; import axios from 'axios'; import { useQuasar } from 'quasar'; -import VnPaginate from 'src/components/ui/VnPaginate.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; -import CardList from 'components/ui/CardList.vue'; import FormModelPopup from 'src/components/FormModelPopup.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnRow from 'src/components/ui/VnRow.vue'; +import VnTable from 'src/components/VnTable/VnTable.vue'; const quasar = useQuasar(); const arrayData = useArrayData('WagonTypeList'); @@ -17,7 +16,7 @@ const store = arrayData.store; const dialog = ref(); const { push } = useRouter(); const { t } = useI18n(); -const paginate = ref(); +const tableRef = ref(); const initialData = computed(() => { return { @@ -25,10 +24,46 @@ const initialData = computed(() => { }; }); -function reloadData() { - initialData.value.name = null; - paginate.value.fetch(); -} +const columns = computed(() => [ + { + align: 'left', + name: 'id', + label: t('ID'), + isId: true, + cardVisible: true, + }, + { + align: 'left', + name: 'name', + label: t('Name'), + isTitle: true, + }, + { + align: 'left', + name: 'divisible', + label: t('Divisible'), + cardVisible: true, + component: 'checkbox', + }, + { + align: 'right', + name: 'tableActions', + actions: [ + { + title: t('components.smartCard.openCard'), + icon: 'arrow_forward', + isPrimary: true, + action: (row) => navigate(row.id, row.name), + }, + { + title: t('wagon.list.remove'), + icon: 'delete', + isPrimary: true, + action: (row) => remove(row), + }, + ], + }, +]); function navigate(id, name) { push({ path: `/wagon/type/${id}/edit`, query: { name } }); @@ -41,51 +76,25 @@ async function remove(row) { type: 'positive', }); store.data.splice(store.data.indexOf(row), 1); + window.location.reload(); } </script> <template> <QPage class="column items-center q-pa-md"> - <div class="vn-card-list"> - <VnPaginate - ref="paginate" - data-key="WagonTypeList" - url="WagonTypes" - order="id DESC" - auto-load - > - <template #body="{ rows }"> - <CardList - v-for="row of rows" - :key="row.id" - :title="(row.name || '').toString()" - :id="row.id" - @click="navigate(row.id, row.name)" - > - <template #list-items> - <QCheckbox - :label="t('Divisble')" - :model-value="row.divisible" - disable - /> - </template> - <template #actions> - <QBtn - :label="t('components.smartCard.openCard')" - @click.stop="navigate(row.id, row.name)" - outline - /> - <QBtn - :label="t('wagon.list.remove')" - @click.stop="remove(row)" - color="primary" - style="margin-top: 15px" - /> - </template> - </CardList> - </template> - </VnPaginate> - </div> + <VnTable + ref="tableRef" + data-key="WagonTypeList" + url="WagonTypes" + :columns="columns" + auto-load + order="id DESC" + :right-search="false" + :column-search="false" + :default-mode="'card'" + :disable-option="{ table: true }" + > + </VnTable> <QPageSticky :offset="[18, 18]"> <QBtn @click.stop="dialog.show()" color="primary" fab icon="add" shortcut="+"> <QDialog ref="dialog"> @@ -94,7 +103,7 @@ async function remove(row) { url-create="WagonTypes" model="WagonType" :form-initial-data="initialData" - @on-data-saved="reloadData()" + @on-data-saved="window.location.reload()" auto-load > <template #form-inputs="{ data }"> diff --git a/src/pages/Wagon/WagonList.vue b/src/pages/Wagon/WagonList.vue index c4824b861..129e803f5 100644 --- a/src/pages/Wagon/WagonList.vue +++ b/src/pages/Wagon/WagonList.vue @@ -1,12 +1,13 @@ <script setup> import axios from 'axios'; import { useQuasar } from 'quasar'; -import VnPaginate from 'src/components/ui/VnPaginate.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; -import CardList from 'components/ui/CardList.vue'; -import VnLv from 'components/ui/VnLv.vue'; +import VnTable from 'src/components/VnTable/VnTable.vue'; +import { computed } from 'vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; +import VnInput from 'src/components/common/VnInput.vue'; const quasar = useQuasar(); const arrayData = useArrayData('WagonList'); @@ -23,14 +24,56 @@ const filter = { }, }; +const columns = computed(() => [ + { + align: 'left', + name: 'label', + label: t('Label'), + isTitle: true, + }, + { + align: 'left', + name: 'plate', + label: t('wagon.list.plate'), + cardVisible: true, + }, + { + align: 'left', + name: 'volume', + label: t('wagon.list.volume'), + cardVisible: true, + }, + { + align: 'left', + name: 'name', + label: t('wagon.list.type'), + cardVisible: true, + format: (row) => row?.type?.name, + }, + { + align: 'right', + name: 'tableActions', + actions: [ + { + title: t('components.smartCard.openCard'), + icon: 'arrow_forward', + isPrimary: true, + action: (row) => navigate(row.id), + }, + { + title: t('wagon.list.remove'), + icon: 'delete', + isPrimary: true, + action: (row) => remove(row), + }, + ], + }, +]); + function navigate(id) { router.push({ path: `/wagon/${id}/edit` }); } -function create() { - router.push({ path: `/wagon/create` }); -} - async function remove(row) { try { await axios.delete(`Wagons/${row.id}`).then(async () => { @@ -39,6 +82,7 @@ async function remove(row) { type: 'positive', }); store.data.splice(store.data.indexOf(row), 1); + window.location.reload(); }); } catch (error) { // @@ -48,53 +92,83 @@ async function remove(row) { <template> <QPage class="column items-center q-pa-md"> - <div class="vn-card-list"> - <VnPaginate - data-key="WagonList" - url="/Wagons" - order="id DESC" - :filter="filter" - auto-load - > - <template #body="{ rows }"> - <CardList - v-for="row of rows" - :key="row.id" - :title="(row.label || '').toString()" - :id="row.id" - @click="navigate(row.id)" - > - <template #list-items> - <VnLv - :label="t('wagon.list.plate')" - :title-label="t('wagon.list.plate')" - :value="row.plate" - /> - <VnLv :label="t('wagon.list.volume')" :value="row?.volume" /> - <VnLv - :label="t('wagon.list.type')" - :value="row?.type?.name" - /> - </template> - <template #actions> - <QBtn - :label="t('components.smartCard.openCard')" - @click.stop="navigate(row.id)" - outline - /> - <QBtn - :label="t('wagon.list.remove')" - @click.stop="remove(row)" - color="primary" - style="margin-top: 15px" - /> - </template> - </CardList> - </template> - </VnPaginate> - </div> - <QPageSticky position="bottom-right" :offset="[18, 18]"> - <QBtn @click="create" fab icon="add" color="primary" shortcut="+" /> - </QPageSticky> + <VnTable + ref="tableRef" + data-key="WagonList" + url="Wagons" + :filter="filter" + :columns="columns" + auto-load + order="id DESC" + :right-search="false" + :column-search="false" + :default-mode="'card'" + :disable-option="{ table: true }" + :create="{ + urlCreate: 'Wagons', + title: t('Create new wagon'), + onDataSaved: () => { + window.location.reload(); + }, + formInitialData: {}, + }" + > + <template #more-create-dialog="{ data }"> + <VnInput + filled + v-model="data.label" + :label="t('wagon.create.label')" + type="number" + min="0" + :rules="[(val) => !!val || t('wagon.warnings.labelNotEmpty')]" + /> + <VnInput + filled + v-model="data.plate" + :label="t('wagon.create.plate')" + :rules="[(val) => !!val || t('wagon.warnings.plateNotEmpty')]" + /> + <VnInput + filled + v-model="data.volume" + :label="t('wagon.create.volume')" + type="number" + min="0" + :rules="[(val) => !!val || t('wagon.warnings.volumeNotEmpty')]" + /> + <VnSelect + url="WagonTypes" + filled + v-model="data.typeFk" + use-input + fill-input + hide-selected + input-debounce="0" + option-label="name" + option-value="id" + emit-value + map-options + :label="t('wagon.create.type')" + :options="filteredWagonTypes" + :rules="[(val) => !!val || t('wagon.warnings.typeNotEmpty')]" + @filter="filterType" + > + <template v-if="data.typeFk" #append> + <QIcon + name="cancel" + @click.stop.prevent="data.typeFk = null" + class="cursor-pointer" + /> + </template> + <template #no-option> + <QItem> + <QItemSection class="text-grey"> + {{ t('wagon.warnings.noData') }} + </QItemSection> + </QItem> + </template> + </VnSelect> + </template> + </VnTable> </QPage> </template> diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue index abf60a078..9ae91f8ce 100644 --- a/src/pages/Worker/Card/WorkerTimeControl.vue +++ b/src/pages/Worker/Card/WorkerTimeControl.vue @@ -2,6 +2,7 @@ import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import { onMounted, ref, computed, onBeforeMount, nextTick, reactive } from 'vue'; +import { axiosNoError } from 'src/boot/axios'; import FetchData from 'components/FetchData.vue'; import WorkerTimeHourChip from 'pages/Worker/Card/WorkerTimeHourChip.vue'; @@ -12,7 +13,6 @@ import WorkerTimeControlCalendar from 'pages/Worker/Card/WorkerTimeControlCalend import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; -import { useRole } from 'src/composables/useRole'; import { useAcl } from 'src/composables/useAcl'; import { useWeekdayStore } from 'src/stores/useWeekdayStore'; import { useStateStore } from 'stores/useStateStore'; @@ -63,13 +63,16 @@ const selectedCalendarDates = ref([]); const selectedDateFormatted = ref(toDateString(defaultDate.value)); const arrayData = useArrayData('workerData'); - +const acl = useAcl(); const worker = computed(() => arrayData.store?.data); - -const isHr = computed(() => useRole().hasAny(['hr'])); - -const canSend = computed(() => useAcl().hasAny('WorkerTimeControl', 'sendMail', 'WRITE')); - +const canSend = computed(() => + acl.hasAny([{ model: 'WorkerTimeControl', props: 'sendMail', accessType: 'WRITE' }]) +); +const canUpdate = computed(() => + acl.hasAny([ + { model: 'WorkerTimeControl', props: 'updateMailState', accessType: 'WRITE' }, + ]) +); const isHimself = computed(() => user.value.id === Number(route.params.id)); const columns = computed(() => { @@ -257,58 +260,32 @@ const fetchHours = async () => { } }; -const fetchWorkerTimeControlMails = async (filter) => { - try { - const { data } = await axios.get('WorkerTimeControlMails', { - params: { filter: JSON.stringify(filter) }, - }); - - return data; - } catch (err) { - console.error('Error fetching worker time control mails'); - } -}; - const fetchWeekData = async () => { + const where = { + year: selectedDate.value.getFullYear(), + week: selectedWeekNumber.value, + }; try { - const filter = { - where: { - workerFk: route.params.id, - year: selectedDate.value ? selectedDate.value?.getFullYear() : null, - week: selectedWeekNumber.value, - }, - }; + const mail = ( + await axiosNoError.get(`Workers/${route.params.id}/mail`, { + params: { filter: { where } }, + }) + ).data[0]; - const data = await fetchWorkerTimeControlMails(filter); - if (!data.length) { - state.value = null; - } else { - const [mail] = data; + if (!mail) state.value = null; + else { state.value = mail.state; reason.value = mail.reason; } - await canBeResend(); + canResend.value = !!( + await axiosNoError.get('WorkerTimeControlMails/count', { params: { where } }) + ).data.count; } catch (err) { console.error('Error fetching week data'); } }; -const canBeResend = async () => { - canResend.value = false; - - const filter = { - where: { - year: selectedDate.value.getFullYear(), - week: selectedWeekNumber.value, - }, - limit: 1, - }; - - const data = await fetchWorkerTimeControlMails(filter); - if (data.length) canResend.value = true; -}; - const setHours = (data) => { for (const weekDay of weekDays.value) { if (data) { @@ -449,7 +426,7 @@ onMounted(async () => { <div> <QBtnGroup push class="q-gutter-x-sm" flat> <QBtn - v-if="isHimself && state" + v-if="canUpdate && state" :label="t('Satisfied')" color="primary" type="submit" @@ -457,7 +434,7 @@ onMounted(async () => { @click="isSatisfied()" /> <QBtn - v-if="isHimself && state" + v-if="canUpdate && state" :label="t('Not satisfied')" color="primary" type="submit" @@ -468,7 +445,7 @@ onMounted(async () => { </QBtnGroup> <QBtnGroup push class="q-gutter-x-sm q-ml-none" flat> <QBtn - v-if="reason && state && (isHimself || isHr)" + v-if="reason && state && canUpdate" :label="t('Reason')" color="primary" type="submit" diff --git a/src/pages/Worker/WorkerCreate.vue b/src/pages/Worker/WorkerCreate.vue index 297aa4182..b51209879 100644 --- a/src/pages/Worker/WorkerCreate.vue +++ b/src/pages/Worker/WorkerCreate.vue @@ -194,7 +194,6 @@ async function autofillBic(worker) { <VnLocation :rules="validate('Worker.postcode')" :roles-allowed-to-create="['deliveryAssistant']" - v-model="data.location" @update:model-value="(location) => handleLocation(data, location)" :disable="formData.isFreelance" > diff --git a/src/pages/Worker/WorkerList.vue b/src/pages/Worker/WorkerList.vue index b8ef3f9d8..4c9964c0b 100644 --- a/src/pages/Worker/WorkerList.vue +++ b/src/pages/Worker/WorkerList.vue @@ -264,7 +264,6 @@ async function autofillBic(worker) { <VnLocation :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :options="postcodesOptions" - v-model="data.location" @update:model-value="(location) => handleLocation(data, location)" :disable="data.isFreelance" > diff --git a/src/router/modules/customer.js b/src/router/modules/customer.js index 5b73ddc84..1b707f1a2 100644 --- a/src/router/modules/customer.js +++ b/src/router/modules/customer.js @@ -175,7 +175,7 @@ export default { path: 'edit', name: 'CustomerAddressEdit', meta: { - title: 'address-edit', + title: 'addressEdit', }, component: () => import( diff --git a/test/cypress/integration/entry/entryDms.spec.js b/test/cypress/integration/entry/entryDms.spec.js index ed4a3d79c..47dcdba9e 100644 --- a/test/cypress/integration/entry/entryDms.spec.js +++ b/test/cypress/integration/entry/entryDms.spec.js @@ -23,7 +23,7 @@ describe('EntryDms', () => { expect(value).to.have.length(newFileTd++); const newRowSelector = `tbody > :nth-child(${newFileTd})`; cy.waitForElement(newRowSelector); - cy.validateRow(newRowSelector, [u, u, u, u, 'ENTRADA ID 1']); + cy.validateRow(newRowSelector, [u, u, u, u, u, 'ENTRADA ID 1']); //Edit new dms const newDescription = 'entry id 1 modified'; @@ -38,7 +38,7 @@ describe('EntryDms', () => { cy.saveCard(); cy.reload(); - cy.validateRow(newRowSelector, [u, u, u, u, newDescription]); + cy.validateRow(newRowSelector, [u, u, u, u, u, newDescription]); }); }); }); diff --git a/test/cypress/integration/ticket/ticketDescriptor.spec.js b/test/cypress/integration/ticket/ticketDescriptor.spec.js index 516b0f13d..8192b7c7c 100644 --- a/test/cypress/integration/ticket/ticketDescriptor.spec.js +++ b/test/cypress/integration/ticket/ticketDescriptor.spec.js @@ -5,7 +5,7 @@ describe('Ticket descriptor', () => { const warehouseValue = ':nth-child(1) > :nth-child(6) > .value > span'; const summaryHeader = '.summaryHeader > div'; const weight = 25; - const weightValue = ':nth-child(10) > .value > span'; + const weightValue = '.summaryBody.row :nth-child(1) > :nth-child(9) > .value > span'; beforeEach(() => { cy.login('developer'); cy.viewport(1920, 1080); diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js index 30c1d6df2..3533a3c1f 100644 --- a/test/cypress/integration/vnComponent/vnLocation.spec.js +++ b/test/cypress/integration/vnComponent/vnLocation.spec.js @@ -18,7 +18,7 @@ describe('VnLocation', () => { cy.get(inputLocation).click(); cy.get(inputLocation).clear(); cy.get(inputLocation).type('al'); - cy.get(locationOptions).should('have.length.at.least', 3); + cy.get(locationOptions).should('have.length.at.least', 4); }); it('input filter location as "ecuador"', function () { cy.get(inputLocation).click(); @@ -33,11 +33,29 @@ describe('VnLocation', () => { cy.login('developer'); cy.visit('/#/supplier/567/fiscal-data', { timeout: 7000 }); cy.waitForElement('.q-form'); - cy.get(createLocationButton).click(); }); + it('Fin by postalCode', () => { + const postCode = '46600'; + const firstOption = '[role="listbox"] .q-item:nth-child(1)'; + + cy.get(inputLocation).click(); + cy.get(inputLocation).clear(); + cy.get(inputLocation).type(postCode); + cy.get(locationOptions).should('have.length.at.least', 2); + cy.get(firstOption).click(); + cy.get('.q-btn-group > .q-btn--standard > .q-btn__content > .q-icon').click(); + cy.reload(); + cy.waitForElement('.q-form'); + cy.get(inputLocation).should( + 'have.value', + '46600 - Valencia(Province one), España' + ); + }); + it('Create postCode', () => { const postCode = '1234475'; const province = 'Valencia'; + cy.get(createLocationButton).click(); cy.get('.q-card > h1').should('have.text', 'New postcode'); cy.get(dialogInputs).eq(0).clear(); cy.get(dialogInputs).eq(0).type(postCode); @@ -54,6 +72,7 @@ describe('VnLocation', () => { it('Create city', () => { const postCode = '9011'; const province = 'Saskatchew'; + cy.get(createLocationButton).click(); cy.get(dialogInputs).eq(0).type(postCode); // city create button cy.get(