feat: refs #8304 added remove option to operator #1195
|
@ -17,7 +17,7 @@ import { useSession } from 'src/composables/useSession';
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const rows = ref();
|
const rows = ref([]);
|
||||||
const dmsRef = ref();
|
const dmsRef = ref();
|
||||||
const formDialog = ref({});
|
const formDialog = ref({});
|
||||||
const token = useSession().getTokenMultimedia();
|
const token = useSession().getTokenMultimedia();
|
||||||
|
@ -389,6 +389,14 @@ defineExpose({
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="info-row q-pa-md text-center"
|
||||||
|
>
|
||||||
|
<h5>
|
||||||
|
{{ t('No data to display') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VnPaginate>
|
</VnPaginate>
|
||||||
<QDialog v-model="formDialog.show">
|
<QDialog v-model="formDialog.show">
|
||||||
|
|
|
@ -1,51 +1,78 @@
|
||||||
import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from 'vitest';
|
import {
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
vi,
|
||||||
|
beforeAll,
|
||||||
|
afterEach,
|
||||||
|
beforeEach,
|
||||||
|
afterAll,
|
||||||
|
} from 'vitest';
|
||||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||||
|
import vnDate from 'src/boot/vnDate';
|
||||||
|
|
||||||
describe('VnNotes', () => {
|
describe('VnNotes', () => {
|
||||||
jtubau marked this conversation as resolved
Outdated
|
|||||||
let vm;
|
let vm;
|
||||||
let wrapper;
|
let wrapper;
|
||||||
let spyFetch;
|
let spyFetch;
|
||||||
let postMock;
|
let postMock;
|
||||||
let expectedBody;
|
let patchMock;
|
||||||
const mockData= {name: 'Tony', lastName: 'Stark', text: 'Test Note', observationTypeFk: 1};
|
let expectedInsertBody;
|
||||||
|
let expectedUpdateBody;
|
||||||
function generateExpectedBody() {
|
const defaultOptions = {
|
||||||
expectedBody = {...vm.$props.body, ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk }};
|
url: '/test',
|
||||||
}
|
body: { name: 'Tony', lastName: 'Stark' },
|
||||||
|
selectType: false,
|
||||||
async function setTestParams(text, observationType, type){
|
saveUrl: null,
|
||||||
vm.newNote.text = text;
|
justInput: false,
|
||||||
vm.newNote.observationTypeFk = observationType;
|
};
|
||||||
wrapper.setProps({ selectType: type });
|
function generateWrapper(
|
||||||
}
|
options = defaultOptions,
|
||||||
|
text = null,
|
||||||
beforeAll(async () => {
|
observationType = null,
|
||||||
vi.spyOn(axios, 'get').mockReturnValue({ data: [] });
|
) {
|
||||||
|
vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
|
||||||
wrapper = createWrapper(VnNotes, {
|
wrapper = createWrapper(VnNotes, {
|
||||||
propsData: {
|
propsData: options,
|
||||||
url: '/test',
|
|
||||||
body: { name: 'Tony', lastName: 'Stark' },
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
wrapper = wrapper.wrapper;
|
wrapper = wrapper.wrapper;
|
||||||
vm = wrapper.vm;
|
vm = wrapper.vm;
|
||||||
});
|
vm.newNote.text = text;
|
||||||
|
vm.newNote.observationTypeFk = observationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSpyFetch() {
|
||||||
|
spyFetch = vi.spyOn(vm.$refs.vnPaginateRef, 'fetch');
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateExpectedBody() {
|
||||||
|
expectedInsertBody = {
|
||||||
|
...vm.$props.body,
|
||||||
|
...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk },
|
||||||
|
};
|
||||||
|
expectedUpdateBody = { ...vm.$props.body, ...{ notes: vm.newNote.text } };
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
postMock = vi.spyOn(axios, 'post').mockResolvedValue(mockData);
|
postMock = vi.spyOn(axios, 'post');
|
||||||
spyFetch = vi.spyOn(vm.vnPaginateRef, 'fetch').mockImplementation(() => vi.fn());
|
patchMock = vi.spyOn(axios, 'patch');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
expectedBody = {};
|
expectedInsertBody = {};
|
||||||
|
expectedUpdateBody = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('insert', () => {
|
describe('insert', () => {
|
||||||
it('should not call axios.post and vnPaginateRef.fetch if newNote.text is null', async () => {
|
it('should not call axios.post and vnPaginateRef.fetch when newNote.text is null', async () => {
|
||||||
await setTestParams( null, null, true );
|
generateWrapper({ selectType: true });
|
||||||
|
createSpyFetch();
|
||||||
|
|
||||||
await vm.insert();
|
await vm.insert();
|
||||||
|
|
||||||
|
@ -53,8 +80,9 @@ describe('VnNotes', () => {
|
||||||
expect(spyFetch).not.toHaveBeenCalled();
|
expect(spyFetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not call axios.post and vnPaginateRef.fetch if newNote.text is empty', async () => {
|
it('should not call axios.post and vnPaginateRef.fetch when newNote.text is empty', async () => {
|
||||||
await setTestParams( "", null, false );
|
generateWrapper(null, '');
|
||||||
|
createSpyFetch();
|
||||||
|
|
||||||
await vm.insert();
|
await vm.insert();
|
||||||
|
|
||||||
|
@ -62,8 +90,9 @@ describe('VnNotes', () => {
|
||||||
expect(spyFetch).not.toHaveBeenCalled();
|
expect(spyFetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is true', async () => {
|
it('should not call axios.post and vnPaginateRef.fetch when observationTypeFk is null and selectType is true', async () => {
|
||||||
await setTestParams( "Test Note", null, true );
|
generateWrapper({ selectType: true }, 'Test Note');
|
||||||
|
createSpyFetch();
|
||||||
|
|
||||||
await vm.insert();
|
await vm.insert();
|
||||||
|
|
||||||
|
@ -71,37 +100,57 @@ describe('VnNotes', () => {
|
||||||
expect(spyFetch).not.toHaveBeenCalled();
|
expect(spyFetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is false', async () => {
|
it('should call axios.post and vnPaginateRef.fetch when observationTypeFk is missing and selectType is false', async () => {
|
||||||
await setTestParams( "Test Note", null, false );
|
generateWrapper(null, 'Test Note');
|
||||||
|
createSpyFetch();
|
||||||
generateExpectedBody();
|
generateExpectedBody();
|
||||||
|
|
||||||
await vm.insert();
|
await vm.insert();
|
||||||
|
|
||||||
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
|
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody);
|
||||||
expect(spyFetch).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is setted and selectType is false', async () => {
|
|
||||||
await setTestParams( "Test Note", 1, false );
|
|
||||||
|
|
||||||
generateExpectedBody();
|
|
||||||
|
|
||||||
await vm.insert();
|
|
||||||
|
|
||||||
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
|
|
||||||
expect(spyFetch).toHaveBeenCalled();
|
expect(spyFetch).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => {
|
it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => {
|
||||||
await setTestParams( "Test Note", 1, true );
|
generateWrapper({ selectType: true }, 'Test Note', 1);
|
||||||
|
createSpyFetch();
|
||||||
generateExpectedBody();
|
generateExpectedBody();
|
||||||
|
|
||||||
await vm.insert();
|
await vm.insert();
|
||||||
|
|
||||||
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
|
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody);
|
||||||
expect(spyFetch).toHaveBeenCalled();
|
expect(spyFetch).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should call axios.patch with saveUrl when saveUrl is set and justInput is true', async () => {
|
||||||
|
generateWrapper({
|
||||||
|
url: '/business',
|
||||||
|
justInput: true,
|
||||||
|
saveUrl: '/saveUrlTest',
|
||||||
|
});
|
||||||
|
generateExpectedBody();
|
||||||
|
|
||||||
|
await vm.update();
|
||||||
|
|
||||||
|
expect(patchMock).toHaveBeenCalledWith(vm.$props.saveUrl, expectedUpdateBody);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call axios.patch with url when saveUrl is not set and justInput is true', async () => {
|
||||||
|
generateWrapper({
|
||||||
|
url: '/business',
|
||||||
|
body: { workerFk: 1110 },
|
||||||
|
justInput: true,
|
||||||
|
});
|
||||||
|
generateExpectedBody();
|
||||||
|
|
||||||
|
await vm.update();
|
||||||
|
|
||||||
|
expect(patchMock).toHaveBeenCalledWith(
|
||||||
|
`${vm.$props.url}/${vm.$props.body.workerFk}`,
|
||||||
|
expectedUpdateBody,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { ref, reactive } from 'vue';
|
import { ref, reactive, useAttrs, computed } from 'vue';
|
||||||
import { onBeforeRouteLeave } from 'vue-router';
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
@ -16,12 +16,22 @@ import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onFetch']);
|
||||||
jsegarra
commented
Tengo dudas de este componente porque le hemos metido la lógica de cuando es input, y tenemos que jugar con 2 condiciones Conclusión: VnNotes depende de 3 condiciones para renderizar que elemento u otro. Desacoplar la lógica Tengo dudas de este componente porque le hemos metido la lógica de cuando es input, y tenemos que jugar con 2 condiciones
Mi propuesta es definir un componente tipo VnObservation que se comporte como cuando dices que es just-input=true, VnNoteType para el Vnelect y VnNote que tenga solo la lógica del textarea
Pero creo que son demasiados cambios para esta PR
Conclusión: VnNotes depende de 3 condiciones para renderizar que elemento u otro. Desacoplar la lógica
alexm
commented
Yo lo dejaria en 2. Un VnNoteInput y un VnNote. Yo lo dejaria en 2. Un VnNoteInput y un VnNote.
Donde el VnNoteInput seria el textArea.
Y el VnNote tener la logica, y este dividido en 2 partes.
Arriba un slot que por defecto contenga el VnNoteInput
Y abajo el VnPaginate
jsegarra
commented
Por tanto, lo dejamos así y creamos tarea con los comentarios que hemos puesto Por tanto, lo dejamos así y creamos tarea con los comentarios que hemos puesto
*Importante*: referenciar la PR y ambos comentarios en la descripción de la tarea. *Indicando*: que la funcionalidad que fusionemos en esta PR debe quedar en el mismo estado. Añadir estos como checklist
|
|||||||
|
|
||||||
|
const $attrs = useAttrs();
|
||||||
|
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
return Object.keys($attrs).includes('required')
|
||||||
|
});
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
url: { type: String, default: null },
|
url: { type: String, default: null },
|
||||||
|
saveUrl: {type: String, default: null},
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
Porque prop y no un attr? Porque prop y no un attr?
|
|||||||
filter: { type: Object, default: () => {} },
|
filter: { type: Object, default: () => {} },
|
||||||
body: { type: Object, default: () => {} },
|
body: { type: Object, default: () => {} },
|
||||||
addNote: { type: Boolean, default: false },
|
addNote: { type: Boolean, default: false },
|
||||||
selectType: { type: Boolean, default: false },
|
selectType: { type: Boolean, default: false },
|
||||||
|
justInput: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -29,6 +39,13 @@ const quasar = useQuasar();
|
||||||
const newNote = reactive({ text: null, observationTypeFk: null });
|
const newNote = reactive({ text: null, observationTypeFk: null });
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
Diría que esta lógica ya existe o está implementada en qFormMixin Diría que esta lógica ya existe o está implementada en qFormMixin
|
|||||||
const observationTypes = ref([]);
|
const observationTypes = ref([]);
|
||||||
const vnPaginateRef = ref();
|
const vnPaginateRef = ref();
|
||||||
|
let originalText;
|
||||||
|
|
||||||
|
function handleClick(e) {
|
||||||
|
if (e.shiftKey && e.key === 'Enter') return;
|
||||||
|
if ($props.justInput) confirmAndUpdate();
|
||||||
|
else insert();
|
||||||
|
}
|
||||||
|
|
||||||
async function insert() {
|
async function insert() {
|
||||||
if (!newNote.text || ($props.selectType && !newNote.observationTypeFk)) return;
|
if (!newNote.text || ($props.selectType && !newNote.observationTypeFk)) return;
|
||||||
|
@ -41,8 +58,36 @@ async function insert() {
|
||||||
await axios.post($props.url, newBody);
|
await axios.post($props.url, newBody);
|
||||||
await vnPaginateRef.value.fetch();
|
await vnPaginateRef.value.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function confirmAndUpdate() {
|
||||||
|
if(!newNote.text && originalText)
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
.onOk(update) .onOk(update)
|
|||||||
|
title: t('New note is empty'),
|
||||||
|
message: t('Are you sure remove this note?'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(update)
|
||||||
|
.onCancel(() => {
|
||||||
|
newNote.text = originalText;
|
||||||
|
});
|
||||||
|
else update();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update() {
|
||||||
|
originalText = newNote.text;
|
||||||
|
const body = $props.body;
|
||||||
|
const newBody = {
|
||||||
|
...body,
|
||||||
|
...{ notes: newNote.text },
|
||||||
|
};
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
ufff...en el limite ufff...en el limite
jtubau
commented
que seria sacar la evaluación a una const y pasar la const como condición? algo asi? que seria sacar la evaluación a una const y pasar la const como condición?
const changesOnNote = (newNote.text && !$props.justInput) || (newNote.text !== originalText) && $props.justInput;
if (changesOnNote)
algo asi?
|
|||||||
|
await axios.patch(`${$props.saveUrl ?? `${$props.url}/${$props.body.workerFk}`}`, newBody);
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (newNote.text)
|
if ((newNote.text && !$props.justInput) || (newNote.text !== originalText) && $props.justInput)
|
||||||
quasar.dialog({
|
quasar.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
@ -53,6 +98,13 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
});
|
});
|
||||||
else next();
|
else next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function fetchData([ data ]) {
|
||||||
|
newNote.text = data?.notes;
|
||||||
|
originalText = data?.notes;
|
||||||
|
emit('onFetch', data);
|
||||||
|
}
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
Si usamos selectType para vincularlo con un VnSelect, porque si lo relacionamos con VnInput no hacemos inputType? Si usamos selectType para vincularlo con un VnSelect, porque si lo relacionamos con VnInput no hacemos inputType?
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
Demasiadas operaciones para mantener en el apartado template Demasiadas operaciones para mantener en el apartado template
Como max. 2
jtubau
commented
function fetchData(data) { <FetchData así mejor no? le pondrías otro nombre? function fetchData(data) {
newNote.text = data[0]?.notes;
originalText = data[0]?.notes;
emit('onFetch', data);
}
<FetchData
v-if="justInput"
:url="url"
:filter="filter"
@on-fetch="(data) => (fetchData(data))"
auto-load
/>
así mejor no? le pondrías otro nombre?
|
|||||||
<FetchData
|
<FetchData
|
||||||
|
@ -62,8 +114,19 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
auto-load
|
auto-load
|
||||||
@on-fetch="(data) => (observationTypes = data)"
|
@on-fetch="(data) => (observationTypes = data)"
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
podemos simplificar con :class="{condicion: "clase CSS"} podemos simplificar con :class="{condicion: "clase CSS"}
|
|||||||
/>
|
/>
|
||||||
<QCard class="q-pa-xs q-mb-lg full-width" v-if="$props.addNote">
|
<FetchData
|
||||||
<QCardSection horizontal>
|
v-if="justInput"
|
||||||
|
:url="url"
|
||||||
|
:filter="filter"
|
||||||
|
@on-fetch="fetchData"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<QCard
|
||||||
|
class="q-pa-xs q-mb-lg full-width"
|
||||||
|
:class="{ 'just-input': $props.justInput }"
|
||||||
|
v-if="$props.addNote || $props.justInput"
|
||||||
|
>
|
||||||
|
<QCardSection horizontal v-if="!$props.justInput">
|
||||||
{{ t('New note') }}
|
{{ t('New note') }}
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="q-px-xs q-my-none q-py-none">
|
<QCardSection class="q-px-xs q-my-none q-py-none">
|
||||||
|
@ -75,19 +138,19 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
v-model="newNote.observationTypeFk"
|
v-model="newNote.observationTypeFk"
|
||||||
option-label="description"
|
option-label="description"
|
||||||
style="flex: 0.15"
|
style="flex: 0.15"
|
||||||
:required="true"
|
:required="isRequired"
|
||||||
@keyup.enter.stop="insert"
|
@keyup.enter.stop="insert"
|
||||||
/>
|
/>
|
||||||
<VnInput
|
<VnInput
|
||||||
v-model.trim="newNote.text"
|
v-model.trim="newNote.text"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:label="t('Add note here...')"
|
:label="$props.justInput && newNote.text ? '' : t('Add note here...')"
|
||||||
filled
|
filled
|
||||||
size="lg"
|
size="lg"
|
||||||
autogrow
|
autogrow
|
||||||
@keyup.enter.stop="insert"
|
@keyup.enter.stop="handleClick"
|
||||||
|
:required="isRequired"
|
||||||
clearable
|
clearable
|
||||||
:required="true"
|
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
@ -95,7 +158,7 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
icon="save"
|
icon="save"
|
||||||
color="primary"
|
color="primary"
|
||||||
flat
|
flat
|
||||||
@click="insert"
|
@click="handleClick"
|
||||||
class="q-mb-xs"
|
class="q-mb-xs"
|
||||||
dense
|
dense
|
||||||
data-cy="saveNote"
|
data-cy="saveNote"
|
||||||
|
@ -106,6 +169,7 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
</QCard>
|
</QCard>
|
||||||
<VnPaginate
|
<VnPaginate
|
||||||
|
v-if="!$props.justInput"
|
||||||
:data-key="$props.url"
|
:data-key="$props.url"
|
||||||
:url="$props.url"
|
:url="$props.url"
|
||||||
order="created DESC"
|
order="created DESC"
|
||||||
|
@ -198,6 +262,11 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.just-input {
|
||||||
|
padding-right: 18px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
|
@ -205,4 +274,6 @@ onBeforeRouteLeave((to, from, next) => {
|
||||||
New note: Nueva nota
|
New note: Nueva nota
|
||||||
Save (Enter): Guardar (Intro)
|
Save (Enter): Guardar (Intro)
|
||||||
Observation type: Tipo de observación
|
Observation type: Tipo de observación
|
||||||
|
New note is empty: La nueva nota esta vacia
|
||||||
|
Are you sure remove this note?: Estas seguro de quitar esta nota?
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -19,23 +19,26 @@ onMounted(() => {
|
||||||
const observer = new MutationObserver(
|
const observer = new MutationObserver(
|
||||||
() =>
|
() =>
|
||||||
(hasContent.value =
|
(hasContent.value =
|
||||||
actions.value?.childNodes?.length + data.value?.childNodes?.length)
|
actions.value?.childNodes?.length + data.value?.childNodes?.length),
|
||||||
);
|
);
|
||||||
if (actions.value) observer.observe(actions.value, opts);
|
if (actions.value) observer.observe(actions.value, opts);
|
||||||
if (data.value) observer.observe(data.value, opts);
|
if (data.value) observer.observe(data.value, opts);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => stateStore.toggleSubToolbar());
|
const actionsChildCount = () => !!actions.value?.childNodes?.length;
|
||||||
|
|
||||||
|
onBeforeUnmount(() => stateStore.toggleSubToolbar() && hasSubToolbar);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
jtubau marked this conversation as resolved
Outdated
jsegarra
commented
no hace falta definir llaves y return no hace falta definir llaves y return
|
|||||||
<template>
|
<template>
|
||||||
<QToolbar
|
<QToolbar
|
||||||
id="subToolbar"
|
id="subToolbar"
|
||||||
class="justify-end sticky"
|
|
||||||
v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
|
v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
|
||||||
|
class="justify-end sticky"
|
||||||
>
|
>
|
||||||
<slot name="st-data">
|
<slot name="st-data">
|
||||||
<div id="st-data"></div>
|
<div id="st-data" :class="{ 'full-width': !actionsChildCount() }">
|
||||||
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<slot name="st-actions">
|
<slot name="st-actions">
|
||||||
|
|
|
@ -23,5 +23,6 @@ const noteFilter = computed(() => {
|
||||||
:body="{ clientFk: route.params.id }"
|
:body="{ clientFk: route.params.id }"
|
||||||
style="overflow-y: auto"
|
style="overflow-y: auto"
|
||||||
:select-type="true"
|
:select-type="true"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { nextTick, ref, watch } from 'vue';
|
import { nextTick, ref, watch, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useAcl } from 'src/composables/useAcl';
|
||||||
|
|
||||||
import WorkerCalendarFilter from 'pages/Worker/Card/WorkerCalendarFilter.vue';
|
import WorkerCalendarFilter from 'pages/Worker/Card/WorkerCalendarFilter.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
@ -9,10 +10,17 @@ import WorkerCalendarItem from 'pages/Worker/Card/WorkerCalendarItem.vue';
|
||||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||||
|
import { useStateStore } from 'src/stores/useStateStore';
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const acl = useAcl();
|
||||||
|
const canSeeNotes = computed(() =>
|
||||||
|
acl.hasAny([{ model: 'Worker', props: '__get__business', accessType: 'READ' }]),
|
||||||
|
);
|
||||||
const workerIsFreelance = ref();
|
const workerIsFreelance = ref();
|
||||||
const WorkerFreelanceRef = ref();
|
const WorkerFreelanceRef = ref();
|
||||||
const workerCalendarFilterRef = ref(null);
|
const workerCalendarFilterRef = ref(null);
|
||||||
|
@ -26,6 +34,10 @@ const contractHolidays = ref(null);
|
||||||
const yearHolidays = ref(null);
|
const yearHolidays = ref(null);
|
||||||
const eventsMap = ref({});
|
const eventsMap = ref({});
|
||||||
const festiveEventsMap = ref({});
|
const festiveEventsMap = ref({});
|
||||||
|
const saveUrl = ref();
|
||||||
|
const body = {
|
||||||
|
workerFk: route.params.id,
|
||||||
jsegarra marked this conversation as resolved
jsegarra
commented
entiendo la buena practica pero al ser 1 sola linea, menor abajo entiendo la buena practica pero al ser 1 sola linea, menor abajo
2 bien, pero 3 ya no
|
|||||||
|
};
|
||||||
|
|
||||||
const onFetchActiveContract = (data) => {
|
const onFetchActiveContract = (data) => {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
@ -67,7 +79,7 @@ const onFetchAbsences = (data) => {
|
||||||
name: holidayName,
|
name: holidayName,
|
||||||
isFestive: true,
|
isFestive: true,
|
||||||
},
|
},
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -146,7 +158,7 @@ watch(
|
||||||
async () => {
|
async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
await activeContractRef.value.fetch();
|
await activeContractRef.value.fetch();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
watch([year, businessFk], () => refreshData());
|
watch([year, businessFk], () => refreshData());
|
||||||
</script>
|
</script>
|
||||||
|
@ -181,6 +193,20 @@ watch([year, businessFk], () => refreshData());
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</RightMenu>
|
</RightMenu>
|
||||||
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown() && canSeeNotes">
|
||||||
|
<VnNotes
|
||||||
|
:just-input="true"
|
||||||
|
:url="`Workers/${route.params.id}/business`"
|
||||||
|
:filter="{ fields: ['id', 'notes', 'workerFk'] }"
|
||||||
|
:save-url="saveUrl"
|
||||||
|
@on-fetch="
|
||||||
|
(data) => {
|
||||||
|
saveUrl = `Businesses/${data.id}`;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:body="body"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
<QPage class="column items-center">
|
<QPage class="column items-center">
|
||||||
<QCard v-if="workerIsFreelance">
|
<QCard v-if="workerIsFreelance">
|
||||||
<QCardSection class="text-center">
|
<QCardSection class="text-center">
|
||||||
|
|
|
@ -180,8 +180,6 @@ const yearList = ref(generateYears());
|
||||||
:is-clearable="false"
|
:is-clearable="false"
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('Contract')"
|
:label="t('Contract')"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, watch } from '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 VnSelect from 'src/components/common/VnSelect.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
@ -19,6 +19,7 @@ const trainsData = ref([]);
|
||||||
const machinesData = ref([]);
|
const machinesData = ref([]);
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const routeId = computed(() => route.params.id);
|
const routeId = computed(() => route.params.id);
|
||||||
|
const selected = ref([]);
|
||||||
jtubau marked this conversation as resolved
jsegarra
commented
No entiendo la propiedad selected que papel hace No entiendo la propiedad selected que papel hace
jtubau
commented
para pasarle a crudModel que operario es y que pueda eliminarlo con el botón del subtoolbar para pasarle a crudModel que operario es y que pueda eliminarlo con el botón del subtoolbar
jsegarra
commented
Okey, veo la necesidad, pero la ejecución es compleja. Okey, veo la necesidad, pero la ejecución es compleja.
Se puede hacer con menos lineas
|
|||||||
|
|
||||||
const initialData = computed(() => {
|
const initialData = computed(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -41,6 +42,21 @@ async function insert() {
|
||||||
await axios.post('Operators', initialData.value);
|
await axios.post('Operators', initialData.value);
|
||||||
crudModelRef.value.reload();
|
crudModelRef.value.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
jsegarra
commented
No veo la utilidad de este watch No veo la utilidad de este watch
|
|||||||
|
() => crudModelRef.value?.formData,
|
||||||
|
(formData) => {
|
||||||
|
if (formData && formData.length) {
|
||||||
|
if (JSON.stringify(selected.value) !== JSON.stringify(formData)) {
|
||||||
|
selected.value = formData;
|
||||||
|
}
|
||||||
|
} else if (selected.value.length > 0) {
|
||||||
|
selected.value = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
jtubau marked this conversation as resolved
jsegarra
commented
Revisar esta sección porque los botones de la subtoolbar se ven raros, mas altos o comprimidos Revisar esta sección porque los botones de la subtoolbar se ven raros, mas altos o comprimidos
|
|||||||
|
@ -67,6 +83,7 @@ async function insert() {
|
||||||
:data-required="{ workerFk: route.params.id }"
|
:data-required="{ workerFk: route.params.id }"
|
||||||
ref="crudModelRef"
|
ref="crudModelRef"
|
||||||
search-url="operator"
|
search-url="operator"
|
||||||
|
:selected="selected"
|
||||||
auto-load
|
auto-load
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
|
|
Hay que darle una vuelta a este único parámetro de la función, lo veo muy largo para estar ahi