0
0
Fork 0

New general improvements

This commit is contained in:
William Buezas 2023-11-30 17:46:07 -03:00
parent babaf47653
commit 5bab462aa4
13 changed files with 283 additions and 310 deletions

View File

@ -6,6 +6,7 @@ import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore';
import { useValidator } from 'src/composables/useValidator';
import useNotify from 'src/composables/useNotify.js';
import SkeletonForm from 'components/ui/SkeletonForm.vue';
const quasar = useQuasar();
@ -13,6 +14,7 @@ const state = useState();
const stateStore = useStateStore();
const { t } = useI18n();
const { validate } = useValidator();
const { notify } = useNotify();
const $props = defineProps({
url: {
@ -31,6 +33,10 @@ const $props = defineProps({
type: String,
default: null,
},
urlCreate: {
type: String,
default: null,
},
defaultActions: {
type: Boolean,
default: true,
@ -39,6 +45,16 @@ const $props = defineProps({
type: Boolean,
default: false,
},
formInitialData: {
type: Object,
default: () => {},
},
observeFormChanges: {
type: Boolean,
default: true,
description:
'Esto se usa principalmente para permitir guardar sin hacer cambios (Útil para la feature de clonar ya que en este caso queremos poder guardar de primeras)',
},
});
const emit = defineEmits(['onFetch']);
@ -48,9 +64,17 @@ defineExpose({
});
onMounted(async () => {
if ($props.autoLoad) {
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
if ($props.formInitialData && !$props.autoLoad) {
state.set($props.model, $props.formInitialData);
} else {
await fetch();
}
// Disparamos el watcher del form después de que se haya cargado la data inicial, si así se desea
if ($props.observeFormChanges) {
startFormWatcher();
}
});
onUnmounted(() => {
@ -58,11 +82,22 @@ onUnmounted(() => {
});
const isLoading = ref(false);
const hasChanges = ref(false);
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
const hasChanges = ref(!$props.observeFormChanges);
const originalData = ref();
const formData = computed(() => state.get($props.model));
const formUrl = computed(() => $props.url);
const startFormWatcher = () => {
watch(
() => formData.value,
(val) => {
if (val) hasChanges.value = true;
},
{ deep: true }
);
};
function tMobile(...args) {
if (!quasar.platform.is.mobile) return t(...args);
}
@ -75,20 +110,26 @@ async function fetch() {
state.set($props.model, data);
originalData.value = data && JSON.parse(JSON.stringify(data));
watch(formData.value, () => (hasChanges.value = true));
emit('onFetch', state.get($props.model));
}
async function save() {
if (!hasChanges.value) {
return quasar.notify({
type: 'negative',
message: t('globals.noChanges'),
});
notify('globals.noChanges', 'negative');
return;
}
isLoading.value = true;
try {
if ($props.urlCreate) {
await axios.post($props.urlCreate, formData.value);
notify('globals.dataCreated', 'positive');
} else {
await axios.patch($props.urlUpdate || $props.url, formData.value);
}
} catch (err) {
notify('errors.create', 'negative');
}
originalData.value = JSON.parse(JSON.stringify(formData.value));
hasChanges.value = false;
@ -99,8 +140,6 @@ function reset() {
state.set($props.model, originalData.value);
originalData.value = JSON.parse(JSON.stringify(originalData.value));
watch(formData.value, () => (hasChanges.value = true));
emit('onFetch', state.get($props.model));
hasChanges.value = false;
}

View File

@ -15,6 +15,7 @@ export default {
logOut: 'Log out',
dataSaved: 'Data saved',
dataDeleted: 'Data deleted',
dataCreated: 'Data created',
add: 'Add',
create: 'Create',
save: 'Save',
@ -45,6 +46,7 @@ export default {
statusBadGateway: 'It seems that the server has fall down',
statusGatewayTimeout: 'Could not contact the server',
userConfig: 'Error fetching user config',
create: 'Error during creation',
},
login: {
title: 'Login',
@ -629,11 +631,6 @@ export default {
create: 'Create',
summary: 'Summary',
},
list: {
clone: 'Clone',
addEntry: 'Add entry',
preview: 'Preview',
},
summary: {
confirmed: 'Confirmed',
entryId: 'Entry Id',
@ -656,8 +653,9 @@ export default {
},
smartCard: {
openCard: 'View card',
openSummary: 'Open summary',
openSummary: 'Preview',
viewDescription: 'View description',
clone: 'Clone',
},
cardDescriptor: {
mainList: 'Main list',

View File

@ -15,6 +15,7 @@ export default {
logOut: 'Cerrar sesión',
dataSaved: 'Datos guardados',
dataDeleted: 'Datos eliminados',
dataCreated: 'Datos creados',
add: 'Añadir',
create: 'Crear',
save: 'Guardar',
@ -45,6 +46,7 @@ export default {
statusBadGateway: 'Parece ser que el servidor ha caído',
statusGatewayTimeout: 'No se ha podido contactar con el servidor',
userConfig: 'Error al obtener configuración de usuario',
create: 'Error al crear',
},
login: {
title: 'Inicio de sesión',
@ -631,11 +633,6 @@ export default {
create: 'Crear',
summary: 'Resumen',
},
list: {
clone: 'Clonar',
addEntry: 'Añadir entrada',
preview: 'Vista previa',
},
summary: {
confirmed: 'Confirmado',
entryId: 'Id entrada',
@ -660,6 +657,7 @@ export default {
openCard: 'Ficha',
openSummary: 'Detalles',
viewDescription: 'Ver descripción',
clone: 'Clonar',
},
cardDescriptor: {
mainList: 'Listado principal',

View File

@ -160,7 +160,7 @@ onUnmounted(() => {
.status-text {
font-size: 14px;
color: #eeeeee;
color: white;
}
.text {

View File

@ -148,7 +148,7 @@ const downloadCsv = (rows) => {
flat
icon="cloud_download"
round
v-bind:class="{ dark_icon: !manageCheckboxes }"
:class="{ dark_icon: !manageCheckboxes }"
>
<QMenu>
<QList padding dense>
@ -178,11 +178,11 @@ const downloadCsv = (rows) => {
/>
</div>
<CardList2
:addElement="addElement"
:add-element="addElement"
:element="row"
:id="row.id"
:isSelected="manageCheckboxes"
:showCheckbox="true"
:is-selected="manageCheckboxes"
:show-checkbox="true"
:key="row.id"
:title="row.ref"
@click="navigate(row.id)"
@ -253,18 +253,15 @@ const downloadCsv = (rows) => {
</style>
<i18n>
{
"en": {
"searchInvoice": "Search issued invoice",
"fileDenied": "Browser denied file download...",
"fileAllowed": "Successful download of CSV file",
"youCanSearchByInvoiceReference": "You can search by invoice reference"
},
"es": {
"searchInvoice": "Buscar factura emitida",
"fileDenied": "El navegador denegó la descarga de archivos...",
"fileAllowed": "Descarga exitosa de archivo CSV",
"youCanSearchByInvoiceReference": "Puedes buscar por referencia de la factura"
}
}
en:
searchInvoice: Search issued invoice
fileDenied: Browser denied file download...
fileAllowed: Successful download of CSV file
youCanSearchByInvoiceReference: You can search by invoice reference
es:
searchInvoice: Buscar factura emitida
fileDenied: El navegador denegó la descarga de archivos...
fileAllowed: Descarga exitosa de archivo CSV
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
</i18n>

View File

@ -313,7 +313,7 @@ onMounted(async () => {
</template>
<template #top-right>
<div class="row justify-start items-center">
<span class="q-mr-md text-grey-7">
<span class="q-mr-md text-results">
{{ rows.length }} {{ t('results') }}
</span>
<QBtn
@ -332,24 +332,18 @@ onMounted(async () => {
<div class="column justify-start items-start full-height">
{{ t(`invoiceOut.negativeBases.${col.label}`) }}
<QInput
:disable="
[
'isActive',
'hasToInvoice',
'isTaxDataChecked',
].includes(col.field)
"
:borderless="
[
'isActive',
'hasToInvoice',
'isTaxDataChecked',
].includes(col.field)
"
:class="{
invisible:
col.field === 'isActive' ||
col.field === 'hasToInvoice' ||
col.field === 'isTaxDataChecked',
}"
dense
standout
outlined
rounded
v-model="filter[col.field]"
type="text"
@keyup.enter="search()"
/>
</div>
</QTh>
@ -387,55 +381,14 @@ onMounted(async () => {
</template>
<style lang="scss" scoped>
.card {
display: flex;
justify-content: center;
width: 100%;
background-color: #292929;
padding: 16px;
.card-section {
display: flex;
flex-direction: column;
padding: 0px;
}
.status-text {
font-size: 14px;
color: #eeeeee;
}
.text {
font-size: 14px;
color: #aaaaaa;
}
}
.col-content {
border-radius: 4px;
padding: 6px 6px 6px 6px;
}
.text-results {
color: var(--vn-label);
}
</style>
<i18n>
{
"en": {
"status": {
"packageInvoicing": "Build packaging tickets",
"invoicing": "Invoicing client",
"stopping": "Stopping process",
"done": "Ended process"
},
"of": "of"
},
"es": {
"status":{
"packageInvoicing": "Generación de tickets de empaque",
"invoicing": "Facturando a cliente",
"stopping": "Deteniendo proceso",
"done": "Proceso detenido",
},
"of": "de"
}
}
</i18n>
<i18n></i18n>

View File

@ -1,22 +1,17 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { ref } from 'vue';
import suppliersService from 'src/services/suppliers.service';
import { useRouter } from 'vue-router';
import { reactive } from 'vue';
import { useStateStore } from 'stores/useStateStore';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
const { t } = useI18n();
const router = useRouter();
const stateStore = useStateStore();
const newSupplierName = ref();
const createSupplier = async () => {
const params = { name: newSupplierName.value };
const response = await suppliersService.createSupplier(params);
if (response.status === 200) router.push({ path: `/supplier/${response.data.id}` });
};
const newSupplierForm = reactive({
name: null,
});
</script>
<template>
@ -30,27 +25,28 @@ const createSupplier = async () => {
</Teleport>
</template>
<QPage class="q-pa-md">
<QForm
@submit="createSupplier()"
class="text-white q-mx-auto"
style="max-width: 800px"
<QPage>
<QToolbar class="bg-vn-dark justify-end">
<div id="st-data"></div>
<QSpace />
<div id="st-actions"></div>
</QToolbar>
<FormModel
url-create="Suppliers/newSupplier"
model="supplier"
:form-initial-data="newSupplierForm"
>
<QCard class="card">
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
v-model="newSupplierName"
v-model="data.name"
:label="t('supplier.create.supplierName')"
class="full-width"
/>
</QCard>
<QBtn
:label="t('globals.create')"
type="submit"
color="primary"
class="q-mt-md"
/>
<QBtn :label="t('globals.cancel')" class="q-mt-md" flat />
</QForm>
</div>
</VnRow>
</template>
</FormModel>
</QPage>
</template>

View File

@ -1,18 +1,19 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { reactive, computed, ref } from 'vue';
import { reactive, ref } from 'vue';
import FetchData from 'components/FetchData.vue';
import { useTravelStore } from 'src/stores/travel';
import { useRouter, useRoute } from 'vue-router';
import { useRoute } from 'vue-router';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import { toDate } from 'src/filters';
import { onBeforeMount } from 'vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const travelStore = useTravelStore();
const newTravelDataForm = reactive({
const newTravelForm = reactive({
ref: null,
agencyModeFk: null,
shipped: null,
@ -22,27 +23,26 @@ const newTravelDataForm = reactive({
});
const agenciesOptions = ref([]);
const viewAction = ref();
const warehousesOptions = ref([]);
onBeforeMount(() => {
// Esto nos permite decirle a FormModel si queremos observar los cambios o no
// Ya que si queremos clonar queremos que nos permita guardar inmediatamente sin realizar cambios en el form
viewAction.value = route.query.travelData ? 'clone' : 'create';
if (route.query.travelData) {
const travelData = JSON.parse(route.query.travelData);
for (let key in newTravelDataForm) {
for (let key in newTravelForm) {
if (key === 'landed' || key === 'shipped') {
newTravelDataForm[key] = travelData[key].substring(0, 10);
newTravelForm[key] = travelData[key].substring(0, 10);
} else {
newTravelDataForm[key] = travelData[key];
newTravelForm[key] = travelData[key];
}
}
}
});
const createTravel = async () => {
const response = await travelStore.createTravel(newTravelDataForm);
if (response.status === 200) router.push({ path: `/travel/${response.data.id}` });
};
const onFetchAgencies = (agencies) => {
agenciesOptions.value = [...agencies];
};
@ -50,93 +50,126 @@ const onFetchAgencies = (agencies) => {
const onFetchWarehouses = (warehouses) => {
warehousesOptions.value = [...warehouses];
};
const canSubmit = computed(() => {
for (const key in newTravelDataForm) {
if (!newTravelDataForm[key]) return false;
}
return true;
});
const redirectToTravelList = () => {
router.push({ name: 'TravelList' });
};
</script>
<template>
<FetchData url="AgencyModes" @on-fetch="(data) => onFetchAgencies(data)" auto-load />
<FetchData url="Warehouses" @on-fetch="(data) => onFetchWarehouses(data)" auto-load />
<QPage class="q-pa-md">
<QForm
@submit="createTravel()"
class="text-white column q-mx-auto"
style="max-width: 800px"
<QPage>
<QToolbar class="bg-vn-dark">
<div id="st-data"></div>
<QSpace />
<div id="st-actions"></div>
</QToolbar>
<FormModel
url-update="Travels"
model="travel"
:form-initial-data="newTravelForm"
:observe-form-changes="viewAction === 'create'"
>
<QCard class="row q-pa-xl full-width card">
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
v-model="newTravelDataForm.ref"
v-model="data.ref"
:label="t('travel.shared.reference')"
filled
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('travel.shared.agency')"
v-model="newTravelDataForm.agencyModeFk"
v-model="data.agencyModeFk"
:options="agenciesOptions"
option-value="agencyFk"
option-label="name"
hide-selected
filled
/>
<QInput
v-model="newTravelDataForm.shipped"
type="date"
filled
mask="date"
:label="t('travel.shared.shipped')"
/>
<QInput
v-model="newTravelDataForm.landed"
type="date"
filled
mask="date"
:label="t('travel.shared.landed')"
/>
<VnSelectFilter
:label="t('travel.shared.wareHouseOut')"
v-model="newTravelDataForm.warehouseOutFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
filled
/>
<VnSelectFilter
:label="t('travel.shared.wareHouseIn')"
v-model="newTravelDataForm.warehouseInFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
filled
/>
</QCard>
<div class="row">
<QBtn
:label="t('globals.create')"
type="submit"
color="primary"
class="q-mt-md"
:disable="!canSubmit"
/>
<QBtn
:label="t('globals.cancel')"
class="q-mt-md"
flat
:disable="!canSubmit"
@click="redirectToTravelList()"
/>
</div>
</QForm>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
rounded
placeholder="dd-mm-aaa"
:label="t('travel.shared.landed')"
:model-value="toDate(data.shipped)"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
>
<QDate v-model="data.shipped">
<div class="row items-center justify-end">
<QBtn
v-close-popup
label="Close"
color="primary"
flat
/>
</div>
</QDate>
</QPopupProxy>
</QIcon>
</template>
</QInput>
</div>
<div class="col">
<QInput
rounded
placeholder="dd-mm-aaa"
:label="t('travel.shared.landed')"
:model-value="toDate(data.landed)"
>
<template #append>
<QIcon name="event" class="cursor-pointer">
<QPopupProxy
cover
transition-show="scale"
transition-hide="scale"
>
<QDate v-model="data.landed">
<div class="row items-center justify-end">
<QBtn
v-close-popup
label="Close"
color="primary"
flat
/>
</div>
</QDate>
</QPopupProxy>
</QIcon>
</template>
</QInput>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('travel.shared.wareHouseOut')"
v-model="data.warehouseOutFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('travel.shared.wareHouseIn')"
v-model="data.warehouseInFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
/>
</div>
</VnRow>
</template>
</FormModel>
</QPage>
</template>

View File

@ -66,7 +66,6 @@ const decrement = (paramsObj, key) => {
<QInput
:label="t('params.search')"
dense
lazy-rules
outlined
rounded
v-model="params.search"
@ -125,7 +124,6 @@ const decrement = (paramsObj, key) => {
type="number"
:label="t('params.scopeDays')"
dense
lazy-rules
outlined
rounded
class="input-number"
@ -153,7 +151,6 @@ const decrement = (paramsObj, key) => {
<QItemSection>
<QInput
dense
lazy-rules
outlined
rounded
placeholder="dd-mm-aaa"
@ -187,7 +184,6 @@ const decrement = (paramsObj, key) => {
<QItemSection>
<QInput
dense
lazy-rules
outlined
rounded
placeholder="dd-mm-aaa"
@ -239,7 +235,6 @@ const decrement = (paramsObj, key) => {
type="number"
:label="t('params.totalEntries')"
dense
lazy-rules
outlined
rounded
min="0"
@ -286,32 +281,27 @@ const decrement = (paramsObj, key) => {
</style>
<i18n>
{
"en": {
"params": {
"search": "Id/Reference",
"agencyModeFk": "Agency",
"warehouseInFk": "Warehouse In",
"warehouseOutFk": "Warehouse Out",
"scopeDays": "Days onward",
"landedFrom": "Landed from",
"landedTo": "Landed to",
"continent": "Continent out",
"totalEntries": "Total entries"
},
},
"es": {
"params":{
"search": "Id/Referencia",
"agencyModeFk": "Agencia",
"warehouseInFk": "Alm. entrada",
"warehouseOutFk": "Alm. salida",
"scopeDays": "Días adelante",
"landedFrom": "Llegada desde",
"landedTo": "Llegada hasta",
"continent": "Cont. Salida",
"totalEntries": "Ent. totales"
},
}
}
en:
params:
search: Id/Reference
agencyModeFk: Agency
warehouseInFk: Warehouse In
warehouseOutFk: Warehouse Out
scopeDays: Days onward
landedFrom: Landed from
landedTo: Landed to
continent: Continent out
totalEntries: Total entries
es:
params:
search: Id/Referencia
agencyModeFk: Agencia
warehouseInFk: Alm. entrada
warehouseOutFk: Alm. salida
scopeDays: Días adelante
landedFrom: Llegada desde
landedTo: Llegada hasta
continent: Cont. Salida
totalEntries: Ent. totales
</i18n>

View File

@ -98,25 +98,23 @@ onMounted(async () => {
</template>
<template #actions>
<QBtn
:label="t('travel.list.clone')"
:label="t('components.smartCard.clone')"
@click.stop="cloneTravel(row)"
color="white"
outline
type="reset"
/>
<QBtn
:label="t('travel.list.addEntry')"
:label="t('addEntry')"
@click.stop="viewSummary(row.id)"
color="white"
outline
style="margin-top: 15px"
/>
<QBtn
:label="t('components.smartCard.openSummary')"
@click.stop="viewSummary(row.id)"
color="primary"
style="margin-top: 15px"
type="submit"
/>
<QBtn
:label="t('travel.list.preview')"
@click.stop="viewSummary(row.id)"
color="primary"
style="margin-top: 15px"
type="submit"
/>
</template>
</CardList2>
@ -140,5 +138,12 @@ onMounted(async () => {
</style>
<i18n>
en:
addEntry: Add entry
es:
addEntry: Añadir entrada
</i18n>

View File

@ -1,14 +0,0 @@
import axios from 'axios';
const suppliersService = {
createSupplier: async (formData) => {
try {
return await axios.post('Suppliers/newSupplier', formData);
} catch (err) {
console.error(`Error creating new supplier`, err);
return err.response;
}
},
};
export default suppliersService;

View File

@ -10,15 +10,6 @@ const travelService = {
}
},
createTravel: async (params) => {
try {
return await axios.patch('Travels', params);
} catch (err) {
console.error(`Error creating travel`, err);
return err.response;
}
},
getTravelEntries: async (param) => {
try {
return await axios.get(`Travels/${param}/getEntries`);

View File

@ -17,19 +17,6 @@ export const useTravelStore = defineStore({
const { data } = await travelService.getTravels();
this.travels = data || [];
},
async createTravel(travelData) {
const params = {
ref: travelData.ref,
agencyModeFk: travelData.agencyModeFk,
warehouseOutFk: travelData.warehouseOutFk,
warehouseInFk: travelData.warehouseInFk,
landed: new Date(travelData.landed),
shipped: new Date(travelData.shipped),
};
return await travelService.createTravel(params);
},
},
getters: {},