refs #5673 feat(CrudModel): separate from FormModel
gitea/salix-front/pipeline/head There was a failure building this commit
Details
gitea/salix-front/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
4207e564be
commit
c139e8034b
|
@ -0,0 +1,339 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import SkeletonTable from 'components/ui/SkeletonTable.vue';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const state = useState();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { validate } = useValidator();
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
model: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
saveUrl: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
primaryKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'id',
|
||||||
|
},
|
||||||
|
dataRequired: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
defaultSave: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
defaultReset: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
defaultRemove: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const hasChanges = ref(false);
|
||||||
|
const originalData = ref();
|
||||||
|
const vnPaginateRef = ref();
|
||||||
|
const formData = computed(() => state.get($props.model));
|
||||||
|
const formUrl = computed(() => $props.url);
|
||||||
|
|
||||||
|
const emit = defineEmits(['onFetch', 'update:selected']);
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
insert,
|
||||||
|
remove,
|
||||||
|
onSubmit,
|
||||||
|
reset,
|
||||||
|
hasChanges,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetch();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
state.unset($props.model);
|
||||||
|
});
|
||||||
|
|
||||||
|
function tMobile(...args) {
|
||||||
|
if (!quasar.platform.is.mobile) return t(...args);
|
||||||
|
}
|
||||||
|
async function fetch(data) {
|
||||||
|
if (data && Array.isArray(data)) {
|
||||||
|
let $index = 0;
|
||||||
|
data.map((d) => (d.$index = $index++));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
state.set($props.model, originalData.value);
|
||||||
|
watch(formData.value, () => (hasChanges.value = true));
|
||||||
|
hasChanges.value = false;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
function filter(value, update, filterOptions) {
|
||||||
|
update(
|
||||||
|
() => {
|
||||||
|
const { options, filterFn, field } = filterOptions;
|
||||||
|
|
||||||
|
options.value = filterFn(options, value, field);
|
||||||
|
},
|
||||||
|
(ref) => {
|
||||||
|
ref.setOptionIndex(-1);
|
||||||
|
ref.moveOptionSelection(1, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
if (!hasChanges.value) {
|
||||||
|
return quasar.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: t('globals.noChanges'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isLoading.value = true;
|
||||||
|
await saveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveChanges(data) {
|
||||||
|
const changes = data || getChanges();
|
||||||
|
try {
|
||||||
|
await axios.post($props.saveUrl || $props.url + '/crud', changes);
|
||||||
|
} catch (e) {
|
||||||
|
return (isLoading.value = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||||
|
hasChanges.value = false;
|
||||||
|
isLoading.value = false;
|
||||||
|
if (changes.creates?.length) await vnPaginateRef.value.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function insert() {
|
||||||
|
const $index = formData.value.length
|
||||||
|
? formData.value[formData.value.length - 1].$index + 1
|
||||||
|
: 0;
|
||||||
|
formData.value.push(Object.assign({ $index }, $props.dataRequired));
|
||||||
|
hasChanges.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function addRemove(ids) {
|
||||||
|
// for (let id of ids) {
|
||||||
|
// const index = removed.value.indexOf(id);
|
||||||
|
// if (index > -1) {
|
||||||
|
// removed.value = removed.value.slice(index, 1);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// removed.value.push(id);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function remove(data) {
|
||||||
|
if (!data.length)
|
||||||
|
return quasar.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: t('globals.noChanges'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const pk = $props.primaryKey;
|
||||||
|
let ids = data.map((d) => d[pk]).filter(Boolean);
|
||||||
|
let preRemove = data.map((d) => (d[pk] ? null : d.$index)).filter(Boolean);
|
||||||
|
let newData = formData.value;
|
||||||
|
|
||||||
|
// addRemove(ids);
|
||||||
|
if (preRemove.length) {
|
||||||
|
newData = newData.filter(
|
||||||
|
(form) => !preRemove.some((index) => index == form.$index)
|
||||||
|
);
|
||||||
|
state.set($props.model, newData);
|
||||||
|
const changes = getChanges();
|
||||||
|
if (!changes.creates?.length && !changes.updates?.length)
|
||||||
|
hasChanges.value = false;
|
||||||
|
}
|
||||||
|
if (ids.length) {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('confirmDeletion'),
|
||||||
|
message: t('confirmDeletionMessage'),
|
||||||
|
newData,
|
||||||
|
ids,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
await saveChanges({ deletes: ids });
|
||||||
|
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
||||||
|
state.set($props.model, newData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
emit('update:selected', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(formUrl, async () => {
|
||||||
|
originalData.value = null;
|
||||||
|
reset();
|
||||||
|
fetch();
|
||||||
|
});
|
||||||
|
|
||||||
|
function getChanges() {
|
||||||
|
const updates = [];
|
||||||
|
const creates = [];
|
||||||
|
|
||||||
|
const pk = $props.primaryKey;
|
||||||
|
|
||||||
|
for (const [i, row] of formData.value.entries()) {
|
||||||
|
if (!row[pk]) {
|
||||||
|
creates.push(row);
|
||||||
|
} else if (originalData.value) {
|
||||||
|
const data = getDifferences(originalData.value[i], row);
|
||||||
|
if (!isEmpty(data)) {
|
||||||
|
updates.push({
|
||||||
|
data,
|
||||||
|
where: { [pk]: row[pk] },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const changes = { updates, creates };
|
||||||
|
|
||||||
|
for (let prop in changes) {
|
||||||
|
if (changes[prop].length === 0) changes[prop] = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDifferences(obj1, obj2) {
|
||||||
|
let diff = {};
|
||||||
|
for (let key in obj1) {
|
||||||
|
if (obj2[key] && obj1[key] !== obj2[key]) {
|
||||||
|
diff[key] = obj2[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let key in obj2) {
|
||||||
|
if (obj1[key] === undefined && obj2[key]) {
|
||||||
|
diff[key] = obj2[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmpty(obj) {
|
||||||
|
if (obj == null) return true;
|
||||||
|
if (obj === undefined) return true;
|
||||||
|
if (Object.keys(obj).length === 0) return true;
|
||||||
|
|
||||||
|
if (obj.length > 0) return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QBanner v-if="hasChanges" class="text-white bg-warning">
|
||||||
|
<QIcon name="warning" size="md" class="q-mr-md" />
|
||||||
|
<span>{{ t('globals.changesToSave') }}</span>
|
||||||
|
</QBanner>
|
||||||
|
<VnPaginate
|
||||||
|
@submit="onSubmit"
|
||||||
|
@reset="reset"
|
||||||
|
:url="url"
|
||||||
|
v-bind="$attrs"
|
||||||
|
@on-fetch="fetch"
|
||||||
|
class="q-pa-md"
|
||||||
|
:skeleton="false"
|
||||||
|
ref="vnPaginateRef"
|
||||||
|
>
|
||||||
|
<template #body v-if="formData">
|
||||||
|
<slot
|
||||||
|
name="body"
|
||||||
|
:rows="formData"
|
||||||
|
:validate="validate"
|
||||||
|
:filter="filter"
|
||||||
|
></slot>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
<SkeletonTable v-if="!formData" />
|
||||||
|
<Teleport to="#st-actions" v-if="stateStore.isSubToolbarShown()">
|
||||||
|
<QBtnGroup push class="q-gutter-x-sm">
|
||||||
|
<slot name="moreActions" />
|
||||||
|
<QBtn
|
||||||
|
:label="tMobile('globals.remove')"
|
||||||
|
color="primary"
|
||||||
|
icon="delete"
|
||||||
|
@click="remove(selected)"
|
||||||
|
:disable="!selected?.length"
|
||||||
|
:title="t('globals.remove')"
|
||||||
|
v-if="$props.defaultRemove"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="tMobile('globals.reset')"
|
||||||
|
color="primary"
|
||||||
|
icon="restart_alt"
|
||||||
|
flat
|
||||||
|
@click="reset"
|
||||||
|
:disable="!hasChanges"
|
||||||
|
:title="t('globals.reset')"
|
||||||
|
v-if="$props.defaultReset"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="tMobile('globals.save')"
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
@click="onSubmit"
|
||||||
|
:disable="!hasChanges"
|
||||||
|
:title="t('globals.save')"
|
||||||
|
v-if="$props.defaultSave"
|
||||||
|
/>
|
||||||
|
</QBtnGroup>
|
||||||
|
</Teleport>
|
||||||
|
<QInnerLoading
|
||||||
|
:showing="isLoading"
|
||||||
|
:label="t('globals.pleaseWait')"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"confirmDeletion": "Confirm deletion",
|
||||||
|
"confirmDeletionMessage": "Are you sure you want to delete this?"
|
||||||
|
},
|
||||||
|
"es": {
|
||||||
|
"confirmDeletion": "Confirmar eliminación",
|
||||||
|
"confirmDeletionMessage": "Seguro que quieres eliminar?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
|
@ -4,12 +4,14 @@ import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useValidator } from 'src/composables/useValidator';
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
|
||||||
const state = useState();
|
const state = useState();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
const { validate } = useValidator();
|
const { validate } = useValidator();
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -29,63 +31,41 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
crud: {
|
|
||||||
type: Boolean,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
primaryKey: {
|
|
||||||
type: String,
|
|
||||||
default: 'id',
|
|
||||||
},
|
|
||||||
dataRequired: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
defaultActions: {
|
defaultActions: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const isLoading = ref(false);
|
|
||||||
const hasChanges = ref(false);
|
|
||||||
const formData = computed(() => state.get($props.model));
|
|
||||||
const originalData = ref();
|
|
||||||
const formUrl = computed(() => $props.url);
|
|
||||||
const stActionsExist = ref(false);
|
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch']);
|
const emit = defineEmits(['onFetch']);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
save,
|
save,
|
||||||
insert,
|
|
||||||
remove,
|
|
||||||
onSubmit,
|
|
||||||
reset,
|
|
||||||
hasChanges,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => await fetch());
|
||||||
await fetch();
|
|
||||||
stActionsExist.value = !!document.querySelector('#st-actions');
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
state.unset($props.model);
|
state.unset($props.model);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const hasChanges = ref(false);
|
||||||
|
const originalData = ref();
|
||||||
|
const formData = computed(() => state.get($props.model));
|
||||||
|
const formUrl = computed(() => $props.url);
|
||||||
|
|
||||||
|
function tMobile(...args) {
|
||||||
|
if (!quasar.platform.is.mobile) return t(...args);
|
||||||
|
}
|
||||||
|
|
||||||
async function fetch() {
|
async function fetch() {
|
||||||
const { data } = await axios.get($props.url, {
|
const { data } = await axios.get($props.url, {
|
||||||
params: { filter: $props.filter },
|
params: { filter: $props.filter },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
let $index = 0;
|
|
||||||
data.map((d) => (d.$index = $index++));
|
|
||||||
}
|
|
||||||
|
|
||||||
state.set($props.model, data);
|
state.set($props.model, data);
|
||||||
originalData.value = JSON.parse(JSON.stringify(data));
|
originalData.value = Object.assign({}, data);
|
||||||
|
|
||||||
watch(formData.value, () => (hasChanges.value = true));
|
watch(formData.value, () => (hasChanges.value = true));
|
||||||
|
|
||||||
|
@ -100,29 +80,24 @@ async function save() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
await axios.patch($props.urlUpdate || $props.url, formData.value);
|
||||||
await axios.patch($props.urlUpdate || $props.url, formData.value);
|
|
||||||
} catch {
|
|
||||||
return (isLoading.value = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
originalData.value = formData.value;
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
state.set($props.model, originalData.value);
|
state.set($props.model, originalData.value);
|
||||||
watch(formData.value, () => (hasChanges.value = true));
|
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
function filter(value, update, filterOptions) {
|
function filter(value, update, filterOptions) {
|
||||||
update(
|
update(
|
||||||
() => {
|
() => {
|
||||||
const { options, filterFn, field } = filterOptions;
|
const { options, filterFn } = filterOptions;
|
||||||
|
|
||||||
options.value = filterFn(options, value, field);
|
options.value = filterFn(options, value);
|
||||||
},
|
},
|
||||||
(ref) => {
|
(ref) => {
|
||||||
ref.setOptionIndex(-1);
|
ref.setOptionIndex(-1);
|
||||||
|
@ -131,167 +106,42 @@ function filter(value, update, filterOptions) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function crudSave() {
|
|
||||||
if (!hasChanges.value) {
|
|
||||||
return quasar.notify({
|
|
||||||
type: 'negative',
|
|
||||||
message: t('globals.noChanges'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
isLoading.value = true;
|
|
||||||
await saveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveChanges() {
|
|
||||||
const changes = getChanges();
|
|
||||||
try {
|
|
||||||
await axios.post($props.urlUpdate || $props.url + '/crud', changes);
|
|
||||||
} catch (e) {
|
|
||||||
return (isLoading.value = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
|
||||||
hasChanges.value = false;
|
|
||||||
isLoading.value = false;
|
|
||||||
if (changes.creates?.length) await fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function insert() {
|
|
||||||
const $index = formData.value[formData.value.length - 1].$index + 1;
|
|
||||||
formData.value.push(Object.assign({ $index }, $props.dataRequired));
|
|
||||||
hasChanges.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// function addRemove(ids) {
|
|
||||||
// for (let id of ids) {
|
|
||||||
// const index = removed.value.indexOf(id);
|
|
||||||
// if (index > -1) {
|
|
||||||
// removed.value = removed.value.slice(index, 1);
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// removed.value.push(id);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
async function remove(data) {
|
|
||||||
if (!data.length)
|
|
||||||
return quasar.notify({
|
|
||||||
type: 'warning',
|
|
||||||
message: t('globals.noChanges'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const pk = $props.primaryKey;
|
|
||||||
let ids = data.map((d) => d[pk]).filter(Boolean);
|
|
||||||
let preRemove = data.map((d) => d.$index).filter(Boolean);
|
|
||||||
let newData = formData.value;
|
|
||||||
|
|
||||||
// addRemove(ids);
|
|
||||||
if (preRemove.length) {
|
|
||||||
newData = newData.filter(
|
|
||||||
(form) => !preRemove.some((index) => index == form.$index)
|
|
||||||
);
|
|
||||||
state.set($props.model, newData);
|
|
||||||
const changes = getChanges();
|
|
||||||
if (!changes.creates?.length && !changes.updates?.length)
|
|
||||||
hasChanges.value = false;
|
|
||||||
}
|
|
||||||
if (ids.length) {
|
|
||||||
await saveChanges({ deletes: ids });
|
|
||||||
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
|
||||||
state.set($props.model, newData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSubmit() {
|
|
||||||
if ($props.crud) return crudSave();
|
|
||||||
return save();
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(formUrl, async () => {
|
watch(formUrl, async () => {
|
||||||
originalData.value = null;
|
originalData.value = null;
|
||||||
reset();
|
reset();
|
||||||
fetch();
|
fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
function getChanges() {
|
|
||||||
const updates = [];
|
|
||||||
const creates = [];
|
|
||||||
|
|
||||||
const pk = $props.primaryKey;
|
|
||||||
|
|
||||||
for (const [i, row] of formData.value.entries()) {
|
|
||||||
if (!row[pk]) {
|
|
||||||
creates.push(row);
|
|
||||||
} else if (originalData.value) {
|
|
||||||
const data = getDifferences(originalData.value[i], row);
|
|
||||||
if (!isEmpty(data)) {
|
|
||||||
updates.push({
|
|
||||||
data,
|
|
||||||
where: { [pk]: row[pk] },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const changes = { updates, creates };
|
|
||||||
|
|
||||||
for (let prop in changes) {
|
|
||||||
if (changes[prop].length === 0) changes[prop] = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDifferences(obj1, obj2) {
|
|
||||||
let diff = {};
|
|
||||||
for (let key in obj1) {
|
|
||||||
if (obj2[key] && obj1[key] !== obj2[key]) {
|
|
||||||
diff[key] = obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let key in obj2) {
|
|
||||||
if (obj1[key] === undefined && obj2[key]) {
|
|
||||||
diff[key] = obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmpty(obj) {
|
|
||||||
if (obj == null) return true;
|
|
||||||
if (obj === undefined) return true;
|
|
||||||
if (Object.keys(obj).length === 0) return true;
|
|
||||||
|
|
||||||
if (obj.length > 0) return false;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QBanner v-if="hasChanges" class="text-white bg-warning">
|
<QBanner v-if="hasChanges" class="text-white bg-warning">
|
||||||
<QIcon name="warning" size="md" class="q-mr-md" />
|
<QIcon name="warning" size="md" class="q-mr-md" />
|
||||||
<span>{{ t('globals.changesToSave') }}</span>
|
<span>{{ t('globals.changesToSave') }}</span>
|
||||||
</QBanner>
|
</QBanner>
|
||||||
<QForm v-if="formData" @submit="onSubmit" @reset="reset" class="q-pa-md">
|
<QForm v-if="formData" @submit="save" @reset="reset" class="q-pa-md">
|
||||||
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
|
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
|
||||||
</QForm>
|
</QForm>
|
||||||
<Teleport to="#st-actions" v-if="stActionsExist">
|
<Teleport to="#st-actions" v-if="stateStore.isSubToolbarShown()">
|
||||||
<div class="row q-gutter-x-sm">
|
<div v-if="$props.defaultActions">
|
||||||
<slot name="moreActions" />
|
<QBtnGoup push class="q-gutter-x-sm">
|
||||||
<div v-if="$props.defaultActions">
|
<slot name="moreActions" />
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('globals.save')"
|
:label="tMobile('globals.reset')"
|
||||||
color="primary"
|
color="primary"
|
||||||
icon="save"
|
icon="restart_alt"
|
||||||
@click="onSubmit"
|
|
||||||
:disable="!hasChanges"
|
|
||||||
/>
|
|
||||||
<QBtn
|
|
||||||
:label="t('globals.reset')"
|
|
||||||
color="primary"
|
|
||||||
class="q-ml-sm"
|
|
||||||
flat
|
flat
|
||||||
@click="reset"
|
@click="reset"
|
||||||
:disable="!hasChanges"
|
:disable="!hasChanges"
|
||||||
|
:title="t('globals.reset')"
|
||||||
/>
|
/>
|
||||||
</div>
|
<QBtn
|
||||||
|
:label="tMobile('globals.save')"
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
@click="save"
|
||||||
|
:disable="!hasChanges"
|
||||||
|
:title="t('globals.save')"
|
||||||
|
/>
|
||||||
|
</QBtnGoup>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<SkeletonForm v-if="!formData" />
|
<SkeletonForm v-if="!formData" />
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<template>
|
||||||
|
<div class="q-pa-md w">
|
||||||
|
<div class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col-1">
|
||||||
|
<QSkeleton type="rect" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="rect" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="rect" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="rect" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="rect" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="rect" square />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-gutter-md q-mb-md" v-for="n in 5" :key="n">
|
||||||
|
<div class="col-1">
|
||||||
|
<QSkeleton type="QInput" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="QInput" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="QInput" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="QInput" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="QInput" square />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSkeleton type="QInput" square />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.w {
|
||||||
|
width: 80vw;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -46,6 +46,10 @@ const props = defineProps({
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 500,
|
default: 500,
|
||||||
},
|
},
|
||||||
|
skeleton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onPaginate']);
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
||||||
|
@ -144,7 +148,10 @@ async function onLoad(...params) {
|
||||||
{{ t('No results found') }}
|
{{ t('No results found') }}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="props.autoLoad && !store.data" class="card-list q-gutter-y-md">
|
<div
|
||||||
|
v-if="props.skeleton && props.autoLoad && !store.data"
|
||||||
|
class="card-list q-gutter-y-md"
|
||||||
|
>
|
||||||
<QCard class="card" v-for="$index in $props.limit" :key="$index">
|
<QCard class="card" v-for="$index in $props.limit" :key="$index">
|
||||||
<QItem v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
<QItem v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
||||||
<QItemSection class="q-pa-md">
|
<QItemSection class="q-pa-md">
|
||||||
|
|
|
@ -35,4 +35,19 @@ body.body--light {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
--vn-text: #000000;
|
||||||
|
--vn-gray: #dddddd;
|
||||||
|
--vn-label: #5f5f5f;
|
||||||
|
--vn-dark: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.body--dark {
|
||||||
|
--vn-text: #ffffff;
|
||||||
|
--vn-gray: #313131;
|
||||||
|
--vn-label: #a8a8a8;
|
||||||
|
--vn-dark: #292929;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-vn-dark {
|
||||||
|
background-color: var(--vn-dark);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ const claimSections = [
|
||||||
let salixUrl;
|
let salixUrl;
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
salixUrl = await getUrl(`claim/${entityId.value}`);
|
salixUrl = await getUrl(`claim/${entityId.value}`);
|
||||||
|
stateStore.setSubtoolbar();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -64,10 +65,8 @@ onMounted(async () => {
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<QPageContainer>
|
<QPageContainer>
|
||||||
<QToolbar id="sub-toolbar" class="bg-dark text-white justify-end">
|
<QToolbar class="bg-vn-dark justify-end">
|
||||||
<div id="st-data">
|
<div id="st-data"></div>
|
||||||
{{ route.meta?.title && t(`claim.pageTitles.${route.meta.title}`) }}
|
|
||||||
</div>
|
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<div id="st-actions"></div>
|
<div id="st-actions"></div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
|
||||||
|
@ -122,27 +122,20 @@ const columns = computed(() => [
|
||||||
/>
|
/>
|
||||||
<div class="column items-center">
|
<div class="column items-center">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<FormModel
|
<CrudModel
|
||||||
|
data-key="ClaimDevelopments"
|
||||||
url="ClaimDevelopments"
|
url="ClaimDevelopments"
|
||||||
:crud="true"
|
|
||||||
:filter="developmentsFilter"
|
|
||||||
model="claimDevelopment"
|
model="claimDevelopment"
|
||||||
|
:filter="developmentsFilter"
|
||||||
ref="claimDevelopmentForm"
|
ref="claimDevelopmentForm"
|
||||||
:data-required="{ claimFk: route.params.id }"
|
:data-required="{ claimFk: route.params.id }"
|
||||||
|
v-model:selected="selected"
|
||||||
|
auto-load
|
||||||
>
|
>
|
||||||
<template #moreActions>
|
<template #body="{ rows }">
|
||||||
<QBtn
|
|
||||||
:label="t('globals.remove')"
|
|
||||||
color="primary"
|
|
||||||
icon="delete"
|
|
||||||
@click="claimDevelopmentForm.remove(selected)"
|
|
||||||
:disable="!selected.length"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #form="{ data }">
|
|
||||||
<QTable
|
<QTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:rows="data"
|
:rows="rows"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
row-key="$index"
|
row-key="$index"
|
||||||
selection="multiple"
|
selection="multiple"
|
||||||
|
@ -187,7 +180,7 @@ const columns = computed(() => [
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</template>
|
</template>
|
||||||
</FormModel>
|
</CrudModel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useQuasar } from 'quasar';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ const linesFilter = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const claimLinesForm = ref();
|
||||||
const claim = ref(null);
|
const claim = ref(null);
|
||||||
async function onFetchClaim(data) {
|
async function onFetchClaim(data) {
|
||||||
claim.value = data;
|
claim.value = data;
|
||||||
|
@ -46,6 +47,7 @@ async function onFetchClaim(data) {
|
||||||
const amount = ref(0);
|
const amount = ref(0);
|
||||||
const amountClaimed = ref(0);
|
const amountClaimed = ref(0);
|
||||||
async function onFetch(rows) {
|
async function onFetch(rows) {
|
||||||
|
if (!rows || rows.length) return;
|
||||||
amount.value = rows.reduce(
|
amount.value = rows.reduce(
|
||||||
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
|
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
|
||||||
0
|
0
|
||||||
|
@ -141,47 +143,6 @@ function onUpdateDiscount(response) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmRemove() {
|
|
||||||
const rows = selected.value;
|
|
||||||
const count = rows.length;
|
|
||||||
|
|
||||||
if (count === 0) {
|
|
||||||
return quasar.notify({
|
|
||||||
message: 'You must select at least one row',
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
quasar
|
|
||||||
.dialog({
|
|
||||||
component: VnConfirm,
|
|
||||||
componentProps: {
|
|
||||||
title: t('Delete claimed sales'),
|
|
||||||
message: t('You are about to remove {count} rows', count, { count }),
|
|
||||||
data: { rows },
|
|
||||||
promise: remove,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.onOk(() => {
|
|
||||||
for (const row of rows) {
|
|
||||||
const orgData = store.data;
|
|
||||||
const index = orgData.findIndex((item) => item.id === row.id);
|
|
||||||
store.data.splice(index, 1);
|
|
||||||
selected.value = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function remove({ rows }) {
|
|
||||||
if (!rows.length) return;
|
|
||||||
const body = { deletes: rows.map((row) => row.id) };
|
|
||||||
await axios.post(`ClaimBeginnings/crud`, body);
|
|
||||||
quasar.notify({
|
|
||||||
type: 'positive',
|
|
||||||
message: t('globals.rowRemoved'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function showImportDialog() {
|
function showImportDialog() {
|
||||||
quasar
|
quasar
|
||||||
.dialog({
|
.dialog({
|
||||||
|
@ -191,10 +152,8 @@ function showImportDialog() {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QPageSticky position="top" :offset="[0, 0]" expand>
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
||||||
<QToolbar class="bg-dark text-white">
|
<QToolbar class="bg-dark text-white">
|
||||||
<QToolbarTitle> {{ t('Claimed lines') }} </QToolbarTitle>
|
|
||||||
<QSpace />
|
|
||||||
<div class="row q-gutter-md">
|
<div class="row q-gutter-md">
|
||||||
<div>
|
<div>
|
||||||
{{ t('Amount') }}
|
{{ t('Amount') }}
|
||||||
|
@ -211,7 +170,7 @@ function showImportDialog() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
</QPageSticky>
|
</Teleport>
|
||||||
|
|
||||||
<FetchData
|
<FetchData
|
||||||
:url="`Claims/${route.params.id}`"
|
:url="`Claims/${route.params.id}`"
|
||||||
|
@ -221,11 +180,16 @@ function showImportDialog() {
|
||||||
/>
|
/>
|
||||||
<div class="column items-center">
|
<div class="column items-center">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<VnPaginate
|
<CrudModel
|
||||||
data-key="ClaimLines"
|
data-key="ClaimLines"
|
||||||
|
ref="claimLinesForm"
|
||||||
:url="`Claims/${route.params.id}/lines`"
|
:url="`Claims/${route.params.id}/lines`"
|
||||||
|
save-url="ClaimBeginnings/crud"
|
||||||
:filter="linesFilter"
|
:filter="linesFilter"
|
||||||
@on-fetch="onFetch"
|
@on-fetch="onFetch"
|
||||||
|
v-model:selected="selected"
|
||||||
|
:default-save="false"
|
||||||
|
:default-reset="false"
|
||||||
auto-load
|
auto-load
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
|
@ -361,46 +325,12 @@ function showImportDialog() {
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</template>
|
</template>
|
||||||
</VnPaginate>
|
</CrudModel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Teleport
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
v-if="stateStore.isHeaderMounted() && !$q.screen.lt.sm"
|
<QBtn fab color="primary" icon="add" @click="showImportDialog()" />
|
||||||
to="#actions-prepend"
|
|
||||||
>
|
|
||||||
<div class="row q-gutter-x-sm">
|
|
||||||
<QBtn
|
|
||||||
v-if="selected.length > 0"
|
|
||||||
@click="confirmRemove"
|
|
||||||
icon="delete"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
rounded
|
|
||||||
>
|
|
||||||
<QTooltip bottom> {{ t('globals.remove') }} </QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
<QBtn @click="showImportDialog" icon="add" color="primary" flat dense rounded>
|
|
||||||
<QTooltip bottom> {{ t('globals.add') }} </QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
<QSeparator vertical />
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
<!-- v-if="quasar.platform.is.mobile" -->
|
|
||||||
<QPageSticky v-if="$q.screen.lt.sm" position="bottom" :offset="[0, 0]" expand>
|
|
||||||
<QToolbar class="bg-primary text-white q-pa-none">
|
|
||||||
<QTabs class="full-width" align="justify" inline-label narrow-indicator>
|
|
||||||
<QTab @click="showImportDialog" icon="add" :label="t('globals.add')" />
|
|
||||||
<QSeparator vertical inset />
|
|
||||||
<QTab
|
|
||||||
@click="confirmRemove"
|
|
||||||
icon="delete"
|
|
||||||
:label="t('globals.remove')"
|
|
||||||
:disable="selected.length === 0"
|
|
||||||
/>
|
|
||||||
</QTabs>
|
|
||||||
</QToolbar>
|
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -421,7 +351,6 @@ en:
|
||||||
You are about to remove <strong>{count}</strong> row |
|
You are about to remove <strong>{count}</strong> row |
|
||||||
You are about to remove <strong>{count}</strong> rows'
|
You are about to remove <strong>{count}</strong> rows'
|
||||||
es:
|
es:
|
||||||
Claimed lines: Líneas reclamadas
|
|
||||||
Delivered: Entregado
|
Delivered: Entregado
|
||||||
Quantity: Cantidad
|
Quantity: Cantidad
|
||||||
Claimed: Reclamada
|
Claimed: Reclamada
|
||||||
|
|
|
@ -5,6 +5,7 @@ export const useStateStore = defineStore('stateStore', () => {
|
||||||
const isMounted = ref(false);
|
const isMounted = ref(false);
|
||||||
const leftDrawer = ref(false);
|
const leftDrawer = ref(false);
|
||||||
const rightDrawer = ref(false);
|
const rightDrawer = ref(false);
|
||||||
|
const subToolbar = ref(false);
|
||||||
|
|
||||||
function toggleLeftDrawer() {
|
function toggleLeftDrawer() {
|
||||||
leftDrawer.value = !leftDrawer.value;
|
leftDrawer.value = !leftDrawer.value;
|
||||||
|
@ -18,6 +19,10 @@ export const useStateStore = defineStore('stateStore', () => {
|
||||||
isMounted.value = true;
|
isMounted.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSubtoolbar() {
|
||||||
|
subToolbar.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
function isHeaderMounted() {
|
function isHeaderMounted() {
|
||||||
return isMounted.value;
|
return isMounted.value;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +35,10 @@ export const useStateStore = defineStore('stateStore', () => {
|
||||||
return rightDrawer.value;
|
return rightDrawer.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSubToolbarShown() {
|
||||||
|
return subToolbar.value;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
leftDrawer,
|
leftDrawer,
|
||||||
rightDrawer,
|
rightDrawer,
|
||||||
|
@ -39,5 +48,7 @@ export const useStateStore = defineStore('stateStore', () => {
|
||||||
toggleRightDrawer,
|
toggleRightDrawer,
|
||||||
isLeftDrawerShown,
|
isLeftDrawerShown,
|
||||||
isRightDrawerShown,
|
isRightDrawerShown,
|
||||||
|
setSubtoolbar,
|
||||||
|
isSubToolbarShown,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
describe('ClaimPhoto', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const claimId = 1;
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/claim/${claimId}/photos`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add new file', () => {
|
||||||
|
cy.get('label > .q-btn').click();
|
||||||
|
cy.get('label > .q-btn input').selectFile('test/cypress/fixtures/image.jpg', {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add new file with drag and drop', () => {
|
||||||
|
cy.get('.container').selectFile('test/cypress/fixtures/image.jpg', {
|
||||||
|
action: 'drag-drop',
|
||||||
|
});
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open first image dialog change to second and close', () => {
|
||||||
|
cy.get(
|
||||||
|
':nth-child(1) > .q-card > .q-img > .q-img__container > .q-img__image'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
|
||||||
|
'be.visible'
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get('.q-carousel__control > .q-btn > .q-btn__content > .q-icon').click();
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'.q-dialog__inner > .q-toolbar > .q-btn > .q-btn__content > .q-icon'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-carousel__slide > .q-img > .q-img__container > .q-img__image').should(
|
||||||
|
'not.be.visible'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove third and fourth file', () => {
|
||||||
|
cy.get(
|
||||||
|
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data deleted');
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
|
||||||
|
).click();
|
||||||
|
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data deleted');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue