refs #5673 feat(CrudModel): separate from FormModel
gitea/salix-front/pipeline/head There was a failure building this commit Details

This commit is contained in:
Alex Moreno 2023-08-16 15:04:16 +02:00
parent 4207e564be
commit c139e8034b
10 changed files with 541 additions and 293 deletions

View File

@ -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>

View File

@ -4,12 +4,14 @@ import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore';
import { useValidator } from 'src/composables/useValidator';
import SkeletonForm from 'components/ui/SkeletonForm.vue';
const quasar = useQuasar();
const { t } = useI18n();
const state = useState();
const stateStore = useStateStore();
const { t } = useI18n();
const { validate } = useValidator();
const $props = defineProps({
@ -29,63 +31,41 @@ const $props = defineProps({
type: String,
default: null,
},
crud: {
type: Boolean,
default: null,
},
primaryKey: {
type: String,
default: 'id',
},
dataRequired: {
type: Object,
default: null,
},
defaultActions: {
type: Boolean,
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']);
defineExpose({
save,
insert,
remove,
onSubmit,
reset,
hasChanges,
});
onMounted(async () => {
await fetch();
stActionsExist.value = !!document.querySelector('#st-actions');
});
onMounted(async () => await fetch());
onUnmounted(() => {
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() {
const { data } = await axios.get($props.url, {
params: { filter: $props.filter },
});
if (Array.isArray(data)) {
let $index = 0;
data.map((d) => (d.$index = $index++));
}
state.set($props.model, data);
originalData.value = JSON.parse(JSON.stringify(data));
originalData.value = Object.assign({}, data);
watch(formData.value, () => (hasChanges.value = true));
@ -100,29 +80,24 @@ async function save() {
});
}
isLoading.value = true;
try {
await axios.patch($props.urlUpdate || $props.url, formData.value);
} catch {
return (isLoading.value = false);
}
await axios.patch($props.urlUpdate || $props.url, formData.value);
originalData.value = JSON.parse(JSON.stringify(formData.value));
originalData.value = formData.value;
hasChanges.value = false;
isLoading.value = false;
}
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;
const { options, filterFn } = filterOptions;
options.value = filterFn(options, value, field);
options.value = filterFn(options, value);
},
(ref) => {
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 () => {
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>
<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>
</QForm>
<Teleport to="#st-actions" v-if="stActionsExist">
<div class="row q-gutter-x-sm">
<slot name="moreActions" />
<div v-if="$props.defaultActions">
<Teleport to="#st-actions" v-if="stateStore.isSubToolbarShown()">
<div v-if="$props.defaultActions">
<QBtnGoup push class="q-gutter-x-sm">
<slot name="moreActions" />
<QBtn
:label="t('globals.save')"
:label="tMobile('globals.reset')"
color="primary"
icon="save"
@click="onSubmit"
:disable="!hasChanges"
/>
<QBtn
:label="t('globals.reset')"
color="primary"
class="q-ml-sm"
icon="restart_alt"
flat
@click="reset"
: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>
</Teleport>
<SkeletonForm v-if="!formData" />

View File

@ -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>

View File

@ -46,6 +46,10 @@ const props = defineProps({
type: Number,
default: 500,
},
skeleton: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(['onFetch', 'onPaginate']);
@ -144,7 +148,10 @@ async function onLoad(...params) {
{{ t('No results found') }}
</h5>
</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">
<QItem v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
<QItemSection class="q-pa-md">

View File

@ -35,4 +35,19 @@ body.body--light {
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);
}

View File

@ -29,6 +29,7 @@ const claimSections = [
let salixUrl;
onMounted(async () => {
salixUrl = await getUrl(`claim/${entityId.value}`);
stateStore.setSubtoolbar();
});
</script>
<template>
@ -64,10 +65,8 @@ onMounted(async () => {
</QScrollArea>
</QDrawer>
<QPageContainer>
<QToolbar id="sub-toolbar" class="bg-dark text-white justify-end">
<div id="st-data">
{{ route.meta?.title && t(`claim.pageTitles.${route.meta.title}`) }}
</div>
<QToolbar class="bg-vn-dark justify-end">
<div id="st-data"></div>
<QSpace />
<div id="st-actions"></div>
</QToolbar>

View File

@ -1,8 +1,8 @@
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FormModel from 'components/FormModel.vue';
import CrudModel from 'components/CrudModel.vue';
import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
@ -122,27 +122,20 @@ const columns = computed(() => [
/>
<div class="column items-center">
<div class="list">
<FormModel
<CrudModel
data-key="ClaimDevelopments"
url="ClaimDevelopments"
:crud="true"
:filter="developmentsFilter"
model="claimDevelopment"
:filter="developmentsFilter"
ref="claimDevelopmentForm"
:data-required="{ claimFk: route.params.id }"
v-model:selected="selected"
auto-load
>
<template #moreActions>
<QBtn
:label="t('globals.remove')"
color="primary"
icon="delete"
@click="claimDevelopmentForm.remove(selected)"
:disable="!selected.length"
/>
</template>
<template #form="{ data }">
<template #body="{ rows }">
<QTable
:columns="columns"
:rows="data"
:rows="rows"
:pagination="{ rowsPerPage: 0 }"
row-key="$index"
selection="multiple"
@ -187,7 +180,7 @@ const columns = computed(() => [
</template>
</QTable>
</template>
</FormModel>
</CrudModel>
</div>
</div>
<QPageSticky position="bottom-right" :offset="[25, 25]">

View File

@ -6,7 +6,7 @@ import { useQuasar } from 'quasar';
import { useRoute } from 'vue-router';
import { useArrayData } from 'composables/useArrayData';
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 VnConfirm from 'components/ui/VnConfirm.vue';
@ -36,6 +36,7 @@ const linesFilter = {
},
};
const claimLinesForm = ref();
const claim = ref(null);
async function onFetchClaim(data) {
claim.value = data;
@ -46,6 +47,7 @@ async function onFetchClaim(data) {
const amount = ref(0);
const amountClaimed = ref(0);
async function onFetch(rows) {
if (!rows || rows.length) return;
amount.value = rows.reduce(
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
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() {
quasar
.dialog({
@ -191,10 +152,8 @@ function showImportDialog() {
}
</script>
<template>
<QPageSticky position="top" :offset="[0, 0]" expand>
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
<QToolbar class="bg-dark text-white">
<QToolbarTitle> {{ t('Claimed lines') }} </QToolbarTitle>
<QSpace />
<div class="row q-gutter-md">
<div>
{{ t('Amount') }}
@ -211,7 +170,7 @@ function showImportDialog() {
</div>
</div>
</QToolbar>
</QPageSticky>
</Teleport>
<FetchData
:url="`Claims/${route.params.id}`"
@ -221,11 +180,16 @@ function showImportDialog() {
/>
<div class="column items-center">
<div class="list">
<VnPaginate
<CrudModel
data-key="ClaimLines"
ref="claimLinesForm"
:url="`Claims/${route.params.id}/lines`"
save-url="ClaimBeginnings/crud"
:filter="linesFilter"
@on-fetch="onFetch"
v-model:selected="selected"
:default-save="false"
:default-reset="false"
auto-load
>
<template #body="{ rows }">
@ -361,46 +325,12 @@ function showImportDialog() {
</template>
</QTable>
</template>
</VnPaginate>
</CrudModel>
</div>
</div>
<Teleport
v-if="stateStore.isHeaderMounted() && !$q.screen.lt.sm"
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 position="bottom-right" :offset="[25, 25]">
<QBtn fab color="primary" icon="add" @click="showImportDialog()" />
</QPageSticky>
</template>
@ -421,7 +351,6 @@ en:
You are about to remove <strong>{count}</strong> row |
You are about to remove <strong>{count}</strong> rows'
es:
Claimed lines: Líneas reclamadas
Delivered: Entregado
Quantity: Cantidad
Claimed: Reclamada

View File

@ -5,6 +5,7 @@ export const useStateStore = defineStore('stateStore', () => {
const isMounted = ref(false);
const leftDrawer = ref(false);
const rightDrawer = ref(false);
const subToolbar = ref(false);
function toggleLeftDrawer() {
leftDrawer.value = !leftDrawer.value;
@ -18,6 +19,10 @@ export const useStateStore = defineStore('stateStore', () => {
isMounted.value = true;
}
function setSubtoolbar() {
subToolbar.value = true;
}
function isHeaderMounted() {
return isMounted.value;
}
@ -30,6 +35,10 @@ export const useStateStore = defineStore('stateStore', () => {
return rightDrawer.value;
}
function isSubToolbarShown() {
return subToolbar.value;
}
return {
leftDrawer,
rightDrawer,
@ -39,5 +48,7 @@ export const useStateStore = defineStore('stateStore', () => {
toggleRightDrawer,
isLeftDrawerShown,
isRightDrawerShown,
setSubtoolbar,
isSubToolbarShown,
};
});

View File

@ -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');
});
});