diff --git a/.vscode/settings.json b/.vscode/settings.json index f175e4b82..5026b7d3b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,13 +14,5 @@ "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, - "cSpell.words": ["axios"], - - "editor.tabSize": 2, - "files.autoSave": "onFocusChange", - "files.trimTrailingWhitespace": true, - "editor.hover.enabled": true, - "editor.formatOnPaste": true, - "editor.wordWrapColumn": 80, - "prettier.singleQuote": true + "cSpell.words": ["axios"] } diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue index 90f19bb59..986358930 100644 --- a/src/components/FormModel.vue +++ b/src/components/FormModel.vue @@ -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,10 +33,28 @@ const $props = defineProps({ type: String, default: null, }, + urlCreate: { + type: String, + default: null, + }, defaultActions: { type: Boolean, default: true, }, + autoLoad: { + 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']); @@ -43,18 +63,41 @@ defineExpose({ save, }); -onMounted(async () => await fetch()); +onMounted(async () => { + // 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(() => { state.unset($props.model); }); 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); } @@ -67,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; - await axios.patch($props.urlUpdate || $props.url, formData.value); + + 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; @@ -91,11 +140,10 @@ 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; } + // eslint-disable-next-line vue/no-dupe-keys function filter(value, update, filterOptions) { update( @@ -176,6 +224,7 @@ watch(formUrl, async () => { max-width: 800px; width: 100%; } + .q-card { padding: 32px; } diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue index 1213c8bbc..b14aae7be 100644 --- a/src/components/common/VnLog.vue +++ b/src/components/common/VnLog.vue @@ -19,6 +19,18 @@ const props = defineProps({ type: String, default: null, }, + url: { + type: String, + default: null, + }, + mapper: { + type: Function, + default: null, + }, + filter: { + type: Object, + default: null, + } }); const columns = [ @@ -65,17 +77,18 @@ function actionColor(action) { - + diff --git a/src/pages/Travel/TravelCreate.vue b/src/pages/Travel/TravelCreate.vue index 6529e3bb2..0f6148c7e 100644 --- a/src/pages/Travel/TravelCreate.vue +++ b/src/pages/Travel/TravelCreate.vue @@ -1,19 +1,19 @@ diff --git a/src/pages/Travel/TravelFilter.vue b/src/pages/Travel/TravelFilter.vue index de7f3c6cd..76c55ab0d 100644 --- a/src/pages/Travel/TravelFilter.vue +++ b/src/pages/Travel/TravelFilter.vue @@ -1,8 +1,8 @@