Merge branch 'dev' into Fix-TicketDescriptorMenuOption
gitea/salix-front/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Jon Elias 2024-12-12 06:52:31 +00:00
commit d7d1e4d691
13 changed files with 218 additions and 42 deletions

View File

@ -1,3 +1,87 @@
# Version 24.50 - 2024-12-10
### Added 🆕
- feat: add reportFileName option by:Javier Segarra
- feat: all clients just with global series by:jgallego
- feat: improve Merge branch 'test' into dev by:Javier Segarra
- feat: manual invoice in two lines by:jgallego
- feat: manualInvoice with address by:jgallego
- feat: randomize functions and example by:Javier Segarra
- feat: refs #6999 added search when user tabs on a filter with value by:Jon
- feat: refs #6999 added tab to search in VnTable filter by:Jon
- feat: refs #7346 #7346 improve form by:Javier Segarra
- feat: refs #7346 address ordered by:jgallego
- feat: refs #7346 radioButton by:jgallego
- feat: refs #7346 style radioButton by:jgallego
- feat: refs #7346 traducciones en cammelCase (7346-manualInvoice) by:jgallego
- feat: refs #8038 added new functionality in VnSelect and refactor styles by:Jon
- feat: refs #8061 #8061 updates by:Javier Segarra
- feat: refs #8087 reactive data by:jorgep
- feat: refs #8087 refs#8087 Redadas en travel by:Carlos Andrés
- feat: refs #8138 add component ticket problems by:pablone
- feat: refs #8163 add max length and more tests by:wbuezas
- feat: refs #8163 add prop by:wbuezas
- feat: refs #8163 add VnInput insert functionality and e2e test by:wbuezas
- feat: refs #8163 limit with maxLength by:Javier Segarra
- feat: refs #8163 maxLength SupplierFD account by:Javier Segarra
- feat: refs #8163 maxLengthVnInput by:Javier Segarra
- feat: refs #8163 use VnAccountNumber in VnAccountNumber by:Javier Segarra
- feat: refs #8166 show notification by:jorgep
### Changed 📦
- feat: refs #8038 added new functionality in VnSelect and refactor styles by:Jon
- perf: add dataCy by:Javier Segarra
- perf: refs #7346 #7346 Imrpove interface dialog by:Javier Segarra
- perf: refs #7346 #7346 use v-show instead v-if by:Javier Segarra
- perf: refs #8036 currentFilter by:alexm
- perf: refs #8061 filter autonomy by:Javier Segarra
- perf: refs #8061 solve conflicts and random posCode it by:Javier Segarra
- perf: refs #8061 use opts from VnSelect by:Javier Segarra
- perf: refs #8163 #8061 createNewPostCodeForm by:Javier Segarra
- perf: remove console by:Javier Segarra
- perf: remove timeout by:Javier Segarra
- perf: test command fillInForm by:Javier Segarra
- refactor: refs #8162 remove comment by:wbuezas
- refactor: remove unnecesary things by:wbuezas
### Fixed 🛠️
- fix: #8016 fetching data by:Javier Segarra
- fix: icons by:jgallego
- fix: refs #7229 download file by:jorgep
- fix: refs #7229 remove catch by:jorgep
- fix: refs #7229 set url by:jorgep
- fix: refs #7229 test by:jorgep
- fix: refs #7229 url by:jorgep
- fix: refs #7229 url + test by:jorgep
- fix: refs #7304 7304 clean warning by:carlossa
- fix: refs #7304 fix list by:carlossa
- fix: refs #7304 fix warning by:carlossa
- fix: refs #7346 traslations by:jgallego
- fix: refs #7529 add save by:carlossa
- fix: refs #7529 fix e2e by:carlossa
- fix: refs #7529 fix front by:carlossa
- fix: refs #7529 fix scss by:carlossa
- fix: refs #7529 fix te2e by:carlossa
- fix: refs #7529 fix workerPit e2e by:carlossa
- fix: refs #7529 front by:carlossa
- fix: refs #8036 apply exprBuilder after save filters by:alexm
- fix: refs #8036 only add where when required by:alexm
- fix: refs #8038 solve conflicts by:Jon
- fix: refs #8061 improve code dependencies (origin/8061_improve_newCP) by:Javier Segarra
- fix: refs #8138 move component from ui folder by:pablone
- fix: refs #8138 sme minor issues by:pablone
- fix: refs #8163 #8061 createNewPostCodeForm by:Javier Segarra
- fix: refs #8163 minor problem when keypress by:Javier Segarra
- fix: refs #8166 show zone error by:jorgep
- fix: removed selectedClient by:jgallego
- refs #7529 fix workerPit by:carlossa
- revert: refs #8061 test #8061 updates by:Javier Segarra
- test: fix own test by:Javier Segarra
- test: refs #8162 #8162 fix TicketList spec by:Javier Segarra
# Version 24.48 - 2024-11-25 # Version 24.48 - 2024-11-25
### Added 🆕 ### Added 🆕

View File

@ -1,20 +1,18 @@
import axios from 'axios';
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import qFormMixin from './qformMixin'; import qFormMixin from './qformMixin';
import keyShortcut from './keyShortcut'; import keyShortcut from './keyShortcut';
import useNotify from 'src/composables/useNotify.js';
import { CanceledError } from 'axios';
import { QForm } from 'quasar'; import { QForm } from 'quasar';
import { QLayout } from 'quasar'; import { QLayout } from 'quasar';
import mainShortcutMixin from './mainShortcutMixin'; import mainShortcutMixin from './mainShortcutMixin';
import { useCau } from 'src/composables/useCau';
const { notify } = useNotify();
export default boot(({ app }) => { export default boot(({ app }) => {
QForm.mixins = [qFormMixin]; QForm.mixins = [qFormMixin];
QLayout.mixins = [mainShortcutMixin]; QLayout.mixins = [mainShortcutMixin];
app.directive('shortcut', keyShortcut); app.directive('shortcut', keyShortcut);
app.config.errorHandler = (error) => { app.config.errorHandler = async (error) => {
let message; let message;
const response = error.response; const response = error.response;
const responseData = response?.data; const responseData = response?.data;
@ -45,12 +43,12 @@ export default boot(({ app }) => {
} }
console.error(error); console.error(error);
if (error instanceof CanceledError) { if (error instanceof axios.CanceledError) {
const env = process.env.NODE_ENV; const env = process.env.NODE_ENV;
if (env && env !== 'development') return; if (env && env !== 'development') return;
message = 'Duplicate request'; message = 'Duplicate request';
} }
notify(message ?? 'globals.error', 'negative', 'error'); await useCau(response, message);
}; };
}); });

View File

@ -25,7 +25,6 @@ const townsFetchDataRef = ref(false);
const townFilter = ref({}); const townFilter = ref({});
const countriesRef = ref(false); const countriesRef = ref(false);
const provincesFetchDataRef = ref(false);
const provincesOptions = ref([]); const provincesOptions = ref([]);
const townsOptions = ref([]); const townsOptions = ref([]);
const town = ref({}); const town = ref({});
@ -71,9 +70,6 @@ async function setProvince(id, data) {
await fetchTowns(); await fetchTowns();
} }
async function onProvinceCreated(data) { async function onProvinceCreated(data) {
await provincesFetchDataRef.value.fetch({
where: { countryFk: postcodeFormData.countryFk },
});
postcodeFormData.provinceFk = data.id; postcodeFormData.provinceFk = data.id;
} }
function provinceByCountry(countryFk = postcodeFormData.countryFk) { function provinceByCountry(countryFk = postcodeFormData.countryFk) {
@ -92,7 +88,6 @@ function setTown(newTown, data) {
data.countryFk = newTown?.province?.countryFk ?? newTown; data.countryFk = newTown?.province?.countryFk ?? newTown;
} }
async function onCityCreated(newTown, formData) { async function onCityCreated(newTown, formData) {
await provincesFetchDataRef.value.fetch();
newTown.province = provincesOptions.value.find( newTown.province = provincesOptions.value.find(
(province) => province.id === newTown.provinceFk (province) => province.id === newTown.provinceFk
); );
@ -125,14 +120,6 @@ async function filterTowns(name) {
</script> </script>
<template> <template>
<FetchData
ref="provincesFetchDataRef"
@on-fetch="handleProvinces"
:sort-by="['name ASC']"
:limit="30"
auto-load
url="Provinces/location"
/>
<FetchData <FetchData
ref="townsFetchDataRef" ref="townsFetchDataRef"
:sort-by="['name ASC']" :sort-by="['name ASC']"
@ -205,6 +192,11 @@ async function filterTowns(name) {
:country-fk="data.countryFk" :country-fk="data.countryFk"
:province-selected="data.provinceFk" :province-selected="data.provinceFk"
@update:model-value="(value) => setProvince(value, data)" @update:model-value="(value) => setProvince(value, data)"
@update:options="
(data) => {
provincesOptions = data;
}
"
v-model="data.provinceFk" v-model="data.provinceFk"
@on-province-created="onProvinceCreated" @on-province-created="onProvinceCreated"
required required

View File

@ -7,7 +7,7 @@ import VnSelectDialog from 'components/common/VnSelectDialog.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import CreateNewProvinceForm from './CreateNewProvinceForm.vue'; import CreateNewProvinceForm from './CreateNewProvinceForm.vue';
const emit = defineEmits(['onProvinceCreated', 'onProvinceFetched']); const emit = defineEmits(['onProvinceCreated', 'onProvinceFetched', 'update:options']);
const $props = defineProps({ const $props = defineProps({
countryFk: { countryFk: {
type: Number, type: Number,
@ -41,6 +41,7 @@ async function onProvinceCreated(_, data) {
} }
async function handleProvinces(data) { async function handleProvinces(data) {
provincesOptions.value = data; provincesOptions.value = data;
emit('update:options', data);
} }
watch( watch(

View File

@ -268,7 +268,7 @@ async function onScroll({ to, direction, from, index }) {
defineExpose({ opts: myOptions }); defineExpose({ opts: myOptions });
function handleKeyDown(event) { function handleKeyDown(event) {
if (event.key === 'Tab') { if (event.key === 'Tab' && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
const inputValue = vnSelectRef.value?.inputValue; const inputValue = vnSelectRef.value?.inputValue;
@ -286,6 +286,17 @@ function handleKeyDown(event) {
} }
vnSelectRef.value?.hidePopup(); vnSelectRef.value?.hidePopup();
} }
const focusableElements = document.querySelectorAll(
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
);
const currentIndex = Array.prototype.indexOf.call(
focusableElements,
event.target
);
if (currentIndex >= 0 && currentIndex < focusableElements.length - 1) {
focusableElements[currentIndex + 1].focus();
}
} }
} }
</script> </script>

73
src/composables/useCau.js Normal file
View File

@ -0,0 +1,73 @@
import VnInput from 'src/components/common/VnInput.vue';
import { useVnConfirm } from 'src/composables/useVnConfirm';
import axios from 'axios';
import { ref } from 'vue';
import { i18n } from 'src/boot/i18n';
import useNotify from 'src/composables/useNotify.js';
export async function useCau(res, message) {
const { notify } = useNotify();
const { openConfirmationModal } = useVnConfirm();
const { config, headers, request, status, statusText, data } = res || {};
const { params, url, method, signal, headers: confHeaders } = config || {};
const { message: resMessage, code, name } = data?.error || {};
const additionalData = {
path: location.hash,
message: resMessage,
code,
request: request?.responseURL,
status,
name,
statusText: statusText,
config: {
url,
method,
params,
headers: confHeaders,
aborted: signal?.aborted,
version: headers?.['salix-version'],
},
};
const opts = {
actions: [
{
icon: 'support_agent',
color: 'primary',
dense: true,
flat: false,
round: true,
handler: async () => {
const locale = i18n.global.t;
const reason = ref(
code == 'ACCESS_DENIED' ? locale('cau.askPrivileges') : ''
);
openConfirmationModal(
locale('cau.title'),
locale('cau.subtitle'),
async () => {
await axios.post('OsTickets/send-to-support', {
reason: reason.value,
additionalData,
});
},
null,
{
component: VnInput,
props: {
modelValue: reason,
'onUpdate:modelValue': (val) => (reason.value = val),
label: locale('cau.inputLabel'),
class: 'full-width',
required: true,
autofocus: true,
},
}
);
},
},
],
};
notify(message ?? 'globals.error', 'negative', 'error', opts);
}

View File

@ -2,7 +2,7 @@ import { Notify } from 'quasar';
import { i18n } from 'src/boot/i18n'; import { i18n } from 'src/boot/i18n';
export default function useNotify() { export default function useNotify() {
const notify = (message, type, icon) => { const notify = (message, type, icon, opts = {}) => {
const defaultIcons = { const defaultIcons = {
warning: 'warning', warning: 'warning',
negative: 'error', negative: 'error',
@ -13,6 +13,7 @@ export default function useNotify() {
message: i18n.global.t(message), message: i18n.global.t(message),
type: type, type: type,
icon: icon ? icon : defaultIcons[type], icon: icon ? icon : defaultIcons[type],
...opts,
}); });
}; };

View File

@ -1,20 +1,27 @@
import { h } from 'vue';
import { Dialog } from 'quasar';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import { useQuasar } from 'quasar';
export function useVnConfirm() { export function useVnConfirm() {
const quasar = useQuasar(); const openConfirmationModal = (
title,
const openConfirmationModal = (title, message, promise, successFn) => { message,
quasar promise,
.dialog({ successFn,
component: VnConfirm, customHTML = {}
componentProps: { ) => {
const { component, props } = customHTML;
Dialog.create({
component: h(
VnConfirm,
{
title: title, title: title,
message: message, message: message,
promise: promise, promise: promise,
}, },
}) { customHTML: () => h(component, props) }
.onOk(async () => { ),
}).onOk(async () => {
if (successFn) successFn(); if (successFn) successFn();
}); });
}; };

View File

@ -369,6 +369,11 @@ resetPassword:
repeatPassword: Repeat password repeatPassword: Repeat password
passwordNotMatch: Passwords don't match passwordNotMatch: Passwords don't match
passwordChanged: Password changed passwordChanged: Password changed
cau:
title: Send cau
subtitle: By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.
inputLabel: Explain why this error should not appear
askPrivileges: Ask for privileges
entry: entry:
list: list:
newEntry: New entry newEntry: New entry

View File

@ -371,6 +371,11 @@ resetPassword:
repeatPassword: Repetir contraseña repeatPassword: Repetir contraseña
passwordNotMatch: Las contraseñas no coinciden passwordNotMatch: Las contraseñas no coinciden
passwordChanged: Contraseña cambiada passwordChanged: Contraseña cambiada
cau:
title: Enviar cau
subtitle: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc
inputLabel: Explique el motivo por el que no deberia aparecer este fallo
askPrivileges: Solicitar permisos
entry: entry:
list: list:
newEntry: Nueva entrada newEntry: Nueva entrada

View File

@ -8,7 +8,7 @@ import { useAcl } from 'src/composables/useAcl';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnChangePassword from 'src/components/common/VnChangePassword.vue'; import VnChangePassword from 'src/components/common/VnChangePassword.vue';
import useNotify from 'src/composables/useNotify.js'; import { useQuasar } from 'quasar';
const $props = defineProps({ const $props = defineProps({
hasAccount: { hasAccount: {
@ -21,7 +21,7 @@ const { t } = useI18n();
const { hasAccount } = toRefs($props); const { hasAccount } = toRefs($props);
const { openConfirmationModal } = useVnConfirm(); const { openConfirmationModal } = useVnConfirm();
const route = useRoute(); const route = useRoute();
const { notify } = useNotify(); const { notify } = useQuasar();
const account = computed(() => useArrayData('AccountId').store.data[0]); const account = computed(() => useArrayData('AccountId').store.data[0]);
account.value.hasAccount = hasAccount.value; account.value.hasAccount = hasAccount.value;
const entityId = computed(() => +route.params.id); const entityId = computed(() => +route.params.id);

View File

@ -101,7 +101,7 @@ const sumRisk = ({ clientRisks }) => {
<VnLv :value="entity.email" copy <VnLv :value="entity.email" copy
><template #label> ><template #label>
{{ t('globals.params.email') }} {{ t('globals.params.email') }}
<VnLinkMail email="entity.email"></VnLinkMail> </template <VnLinkMail :email="entity.email"></VnLinkMail> </template
></VnLv> ></VnLv>
<VnLv <VnLv
:label="t('customer.summary.salesPerson')" :label="t('customer.summary.salesPerson')"

View File

@ -223,10 +223,10 @@ function navigate(id) {
router.push({ path: `/route/${id}` }); router.push({ path: `/route/${id}` });
} }
const cloneRoutes = () => { const cloneRoutes = async () => {
if (!selectedRows.value.length || !startingDate.value) return; if (!selectedRows.value.length || !startingDate.value) return;
axios.post('Routes/clone', { await axios.post('Routes/clone', {
created: startingDate.value, dated: startingDate.value,
ids: selectedRows.value.map((row) => row?.id), ids: selectedRows.value.map((row) => row?.id),
}); });
startingDate.value = null; startingDate.value = null;
@ -274,7 +274,6 @@ const openTicketsDialog = (id) => {
<QCardSection> <QCardSection>
<p class="text-h6 q-ma-none">{{ t('route.Select the starting date') }}</p> <p class="text-h6 q-ma-none">{{ t('route.Select the starting date') }}</p>
</QCardSection> </QCardSection>
<QCardSection class="q-pt-none"> <QCardSection class="q-pt-none">
<VnInputDate <VnInputDate
:label="t('route.Stating date')" :label="t('route.Stating date')"