Merge pull request '4998-claim_photos' (!38) from 4998-claim_photos into dev
gitea/salix-front/pipeline/head This commit looks good
Details
gitea/salix-front/pipeline/head This commit looks good
Details
Reviewed-on: #38 Reviewed-by: Joan Sanchez <joan@verdnatura.es>
This commit is contained in:
commit
de36f9987b
|
@ -57,6 +57,7 @@ module.exports = {
|
||||||
// add your custom rules here
|
// add your custom rules here
|
||||||
rules: {
|
rules: {
|
||||||
'prefer-promise-reject-errors': 'off',
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
'no-unused-vars': 'warn',
|
||||||
|
|
||||||
// allow debugger during development only
|
// allow debugger during development only
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
|
|
@ -2,7 +2,7 @@ const { defineConfig } = require('cypress');
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'http://localhost:8080/',
|
baseUrl: 'http://localhost:9000/',
|
||||||
fixturesFolder: 'test/cypress/fixtures',
|
fixturesFolder: 'test/cypress/fixtures',
|
||||||
screenshotsFolder: 'test/cypress/screenshots',
|
screenshotsFolder: 'test/cypress/screenshots',
|
||||||
supportFile: 'test/cypress/support/index.js',
|
supportFile: 'test/cypress/support/index.js',
|
||||||
|
|
|
@ -34,19 +34,26 @@ async function confirm() {
|
||||||
<q-dialog ref="dialogRef" persistent>
|
<q-dialog ref="dialogRef" persistent>
|
||||||
<q-card class="q-pa-sm">
|
<q-card class="q-pa-sm">
|
||||||
<q-card-section class="row items-center q-pb-none">
|
<q-card-section class="row items-center q-pb-none">
|
||||||
<span class="text-h6 text-grey">{{ t('sendEmailNotification') }}</span>
|
<span class="text-h6 text-grey">{{
|
||||||
|
t('Send email notification: Send email notification')
|
||||||
|
}}</span>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn icon="close" flat round dense v-close-popup />
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="row items-center">
|
<q-card-section class="row items-center">
|
||||||
{{ t('notifyAddress') }}
|
{{ t('The notification will be sent to the following address') }}
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="q-pt-none">
|
<q-card-section class="q-pt-none">
|
||||||
<q-input dense v-model="address" rounded outlined autofocus />
|
<q-input dense v-model="address" rounded outlined autofocus />
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
||||||
<q-btn :label="t('globals.confirm')" color="primary" :loading="isLoading" @click="confirm" />
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click="confirm"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
@ -59,14 +66,7 @@ async function confirm() {
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
{
|
es:
|
||||||
"en": {
|
Send email notification: Enviar notificación por correo,
|
||||||
"sendEmailNotification": "Send email notification",
|
The notification will be sent to the following address: La notificación se enviará a la siguiente dirección
|
||||||
"notifyAddress": "The notification will be sent to the following address"
|
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"sendEmailNotification": "Enviar notificación por correo",
|
|
||||||
"notifyAddress": "La notificación se enviará a la siguiente dirección"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -3,30 +3,42 @@ import { ref } from 'vue';
|
||||||
import { useDialogPluginComponent } from 'quasar';
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const $props = defineProps({
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
question: {
|
question: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: null,
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const question = ref($props.question);
|
const question = props.question || t('question');
|
||||||
const message = ref($props.question);
|
const message = props.message || t('message');
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-dialog ref="dialogRef" persistent>
|
<q-dialog ref="dialogRef" persistent>
|
||||||
<q-card class="q-pa-sm">
|
<q-card class="q-pa-sm">
|
||||||
<q-card-section class="row items-center q-pb-none">
|
<q-card-section class="row items-center q-pb-none q-gutter-md">
|
||||||
|
<q-avatar
|
||||||
|
:icon="icon"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
size="xl"
|
||||||
|
v-if="icon"
|
||||||
|
/>
|
||||||
<span class="text-h6 text-grey">{{ message }}</span>
|
<span class="text-h6 text-grey">{{ message }}</span>
|
||||||
<q-space />
|
<q-space />
|
||||||
<q-btn icon="close" flat round dense v-close-popup />
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
|
@ -36,7 +48,12 @@ const isLoading = ref(false);
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup />
|
||||||
<q-btn :label="t('globals.confirm')" color="primary" :loading="isLoading" @click="onDialogOK" />
|
<q-btn
|
||||||
|
:label="t('globals.confirm')"
|
||||||
|
color="primary"
|
||||||
|
:loading="isLoading"
|
||||||
|
@click="onDialogOK()"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
@ -47,3 +64,15 @@ const isLoading = ref(false);
|
||||||
min-width: 350px;
|
min-width: 350px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
"en": {
|
||||||
|
"question": "Are you sure you want to continue?",
|
||||||
|
"message": "Confirm"
|
||||||
|
}
|
||||||
|
|
||||||
|
"es": {
|
||||||
|
"question": "¿Seguro que quieres continuar?",
|
||||||
|
"message": "Confirmar"
|
||||||
|
}
|
||||||
|
</i18n>
|
|
@ -241,6 +241,7 @@ export default {
|
||||||
summary: 'Summary',
|
summary: 'Summary',
|
||||||
basicData: 'Basic Data',
|
basicData: 'Basic Data',
|
||||||
rma: 'RMA',
|
rma: 'RMA',
|
||||||
|
photos: 'Photos',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
customer: 'Customer',
|
customer: 'Customer',
|
||||||
|
@ -294,6 +295,11 @@ export default {
|
||||||
picked: 'Picked',
|
picked: 'Picked',
|
||||||
returnOfMaterial: 'Return of material authorization (RMA)',
|
returnOfMaterial: 'Return of material authorization (RMA)',
|
||||||
},
|
},
|
||||||
|
photo: {
|
||||||
|
fileDescription: 'Claim id {claimId} from client {clientName} id {clientId}',
|
||||||
|
noData: 'There are no images/videos, click here or drag and drop the file',
|
||||||
|
dragDrop: 'Drag and drop it here',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
invoiceOut: {
|
invoiceOut: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default {
|
||||||
darkMode: 'Modo oscuro',
|
darkMode: 'Modo oscuro',
|
||||||
logOut: 'Cerrar sesión',
|
logOut: 'Cerrar sesión',
|
||||||
dataSaved: 'Datos guardados',
|
dataSaved: 'Datos guardados',
|
||||||
dataDeleted: 'Data deleted',
|
dataDeleted: 'Datos eliminados',
|
||||||
add: 'Añadir',
|
add: 'Añadir',
|
||||||
create: 'Crear',
|
create: 'Crear',
|
||||||
save: 'Guardar',
|
save: 'Guardar',
|
||||||
|
@ -240,6 +240,7 @@ export default {
|
||||||
summary: 'Resumen',
|
summary: 'Resumen',
|
||||||
basicData: 'Datos básicos',
|
basicData: 'Datos básicos',
|
||||||
rma: 'RMA',
|
rma: 'RMA',
|
||||||
|
photos: 'Fotos',
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
customer: 'Cliente',
|
customer: 'Cliente',
|
||||||
|
@ -293,6 +294,12 @@ export default {
|
||||||
picked: 'Recogida',
|
picked: 'Recogida',
|
||||||
returnOfMaterial: 'Autorización de retorno de materiales (RMA)',
|
returnOfMaterial: 'Autorización de retorno de materiales (RMA)',
|
||||||
},
|
},
|
||||||
|
photo: {
|
||||||
|
fileDescription:
|
||||||
|
'Reclamacion ID {claimId} del cliente {clientName} id {clientId}',
|
||||||
|
noData: 'No hay imágenes/videos, haz click aquí o arrastra y suelta el archivo',
|
||||||
|
dragDrop: 'Arrástralo y sueltalo aquí',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
invoiceOut: {
|
invoiceOut: {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
|
|
|
@ -0,0 +1,376 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
import TeleportSlot from 'src/components/ui/TeleportSlot.vue';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const claimId = computed(() => router.currentRoute.value.params.id);
|
||||||
|
|
||||||
|
const claimDms = ref([
|
||||||
|
{
|
||||||
|
dmsFk: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const client = ref({});
|
||||||
|
|
||||||
|
const inputFile = ref();
|
||||||
|
const files = ref({});
|
||||||
|
|
||||||
|
const claimDmsRef = ref();
|
||||||
|
const dmsType = ref({});
|
||||||
|
const config = ref({});
|
||||||
|
const dragFile = ref(false);
|
||||||
|
const dragFileTimeout = ref();
|
||||||
|
const claimDmsFilter = ref({
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'claimDms',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'dms',
|
||||||
|
scope: {
|
||||||
|
fields: ['contentType'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
where: { id: claimId.value },
|
||||||
|
});
|
||||||
|
|
||||||
|
const multimediaDialog = ref();
|
||||||
|
const multimediaSlide = ref();
|
||||||
|
|
||||||
|
function openDialog(dmsId) {
|
||||||
|
multimediaSlide.value = dmsId;
|
||||||
|
multimediaDialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewDeleteDms(dmsId) {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
message: t('This file will be deleted'),
|
||||||
|
icon: 'delete',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => deleteDms(dmsId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteDms(index) {
|
||||||
|
const dmsId = claimDms.value[index].dmsFk;
|
||||||
|
await axios.post(`ClaimDms/${dmsId}/removeFile`);
|
||||||
|
|
||||||
|
claimDms.value.splice(index, 1);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataDeleted'),
|
||||||
|
type: 'positive',
|
||||||
|
icon: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setClaimDms(data) {
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
claimDms.value = data.claimDms.map((media) => {
|
||||||
|
media.isVideo = media.dms.contentType == 'video/mp4';
|
||||||
|
media.url = `${window.location.origin}/api/Claims/${media.dmsFk}/downloadFile?access_token=${token}`;
|
||||||
|
return media;
|
||||||
|
});
|
||||||
|
client.value = data.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
const formData = new FormData();
|
||||||
|
const inputFiles = files.value;
|
||||||
|
for (let i = 0; i < inputFiles.length; i++)
|
||||||
|
formData.append(inputFiles[i].name, inputFiles[i]);
|
||||||
|
|
||||||
|
const query = `claims/${claimId.value}/uploadFile`;
|
||||||
|
|
||||||
|
const dms = {
|
||||||
|
hasFile: false,
|
||||||
|
hasFileAttached: false,
|
||||||
|
reference: claimId.value,
|
||||||
|
warehouseId: config.value.warehouseFk,
|
||||||
|
companyId: config.value.companyFk,
|
||||||
|
dmsTypeId: dmsType.value.id,
|
||||||
|
description: t('claim.photo.fileDescription', {
|
||||||
|
claimId: claimId.value,
|
||||||
|
clientName: client.value.name,
|
||||||
|
clientId: client.value.id,
|
||||||
|
}).toUpperCase(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.post(query, formData, {
|
||||||
|
params: dms,
|
||||||
|
});
|
||||||
|
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataSaved'),
|
||||||
|
type: 'positive',
|
||||||
|
icon: 'check',
|
||||||
|
});
|
||||||
|
|
||||||
|
claimDmsRef.value.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrop($data) {
|
||||||
|
dragFile.value = false;
|
||||||
|
files.value = $data.dataTransfer.files;
|
||||||
|
create();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrag() {
|
||||||
|
clearTimeout(dragFileTimeout.value);
|
||||||
|
dragFileTimeout.value = setTimeout(() => (dragFile.value = false), 500);
|
||||||
|
dragFile.value = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<fetch-data
|
||||||
|
url="Claims"
|
||||||
|
:filter="claimDmsFilter"
|
||||||
|
@on-fetch="([data]) => setClaimDms(data)"
|
||||||
|
limit="20"
|
||||||
|
auto-load
|
||||||
|
ref="claimDmsRef"
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
url="DmsTypes/findOne"
|
||||||
|
:filter="{ where: { code: 'claim' } }"
|
||||||
|
@on-fetch="(data) => (dmsType = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<fetch-data
|
||||||
|
url="UserConfigs/getUserConfig"
|
||||||
|
@on-fetch="(data) => (config = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
:class="['container', { dragFile }]"
|
||||||
|
@drop.prevent="onDrop"
|
||||||
|
@dragenter.prevent
|
||||||
|
@dragover.prevent="onDrag"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-center items-center text-grey q-mt-md column"
|
||||||
|
v-if="dragFile"
|
||||||
|
>
|
||||||
|
<q-icon size="xl" name="file_download" />
|
||||||
|
<h5>
|
||||||
|
{{ t('claim.photo.dragDrop') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-center text-grey q-mt-md cursor-pointer"
|
||||||
|
v-if="!claimDms?.length && !dragFile"
|
||||||
|
@click="inputFile.nativeEl.click()"
|
||||||
|
>
|
||||||
|
<q-icon size="xl" name="image"></q-icon>
|
||||||
|
<q-icon size="xl" name="movie"></q-icon>
|
||||||
|
<h5>
|
||||||
|
{{ t('claim.photo.noData') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="multimediaParent bg-transparent" v-if="claimDms?.length && !dragFile">
|
||||||
|
<div
|
||||||
|
v-for="(media, index) of claimDms"
|
||||||
|
:key="index"
|
||||||
|
class="relative-position"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
icon="delete"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
size="md"
|
||||||
|
class="all-pointer-events absolute delete-button zindex"
|
||||||
|
@click.stop="viewDeleteDms(index)"
|
||||||
|
round
|
||||||
|
/>
|
||||||
|
<q-icon
|
||||||
|
name="play_circle"
|
||||||
|
color="primary"
|
||||||
|
size="xl"
|
||||||
|
class="absolute-center zindex"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click.stop="openDialog(media.dmsFk)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Video</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
<q-card class="multimedia relative-position">
|
||||||
|
<q-img
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
>
|
||||||
|
</q-img>
|
||||||
|
<video
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
muted="muted"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
/>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<teleport-slot v-if="!quasar.platform.is.mobile" to="#header-actions">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<label for="fileInput">
|
||||||
|
<q-btn
|
||||||
|
@click="inputFile.nativeEl.click()"
|
||||||
|
icon="add"
|
||||||
|
color="primary"
|
||||||
|
dense
|
||||||
|
rounded
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
ref="inputFile"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
multiple
|
||||||
|
v-model="files"
|
||||||
|
@update:model-value="create()"
|
||||||
|
/>
|
||||||
|
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</label>
|
||||||
|
<q-separator vertical />
|
||||||
|
</div>
|
||||||
|
</teleport-slot>
|
||||||
|
|
||||||
|
<teleport-slot to=".q-footer">
|
||||||
|
<q-tabs align="justify" inline-label narrow-indicator>
|
||||||
|
<q-tab
|
||||||
|
@click="inputFile.nativeEl.click()"
|
||||||
|
icon="add_circle"
|
||||||
|
:label="t('globals.add')"
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
ref="inputFile"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
multiple
|
||||||
|
v-model="files"
|
||||||
|
@update:model-value="create()"
|
||||||
|
/>
|
||||||
|
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||||
|
</q-tab>
|
||||||
|
</q-tabs>
|
||||||
|
</teleport-slot>
|
||||||
|
|
||||||
|
<!-- MULTIMEDIA DIALOG START-->
|
||||||
|
<q-dialog
|
||||||
|
v-model="multimediaDialog"
|
||||||
|
transition-show="slide-up"
|
||||||
|
transition-hide="slide-down"
|
||||||
|
>
|
||||||
|
<q-toolbar class="absolute zindex close-button">
|
||||||
|
<q-space />
|
||||||
|
<q-btn icon="close" color="primary" round dense v-close-popup />
|
||||||
|
</q-toolbar>
|
||||||
|
<q-carousel swipeable animated v-model="multimediaSlide" arrows class="fit">
|
||||||
|
<q-carousel-slide
|
||||||
|
v-for="media of claimDms"
|
||||||
|
:key="media.dmsFk"
|
||||||
|
:name="media.dmsFk"
|
||||||
|
>
|
||||||
|
<q-img
|
||||||
|
:src="media.url"
|
||||||
|
class="fit"
|
||||||
|
fit="scale-down"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
/>
|
||||||
|
<video class="q-ma-none fit" v-if="media.isVideo" controls muted autoplay>
|
||||||
|
<source :src="media.url" type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</q-carousel-slide>
|
||||||
|
</q-carousel>
|
||||||
|
</q-dialog>
|
||||||
|
<!-- MULTIMEDIA DIALOG END-->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
min-height: 80vh;
|
||||||
|
min-width: 80%;
|
||||||
|
}
|
||||||
|
.q-dialog__inner--minimized > div {
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
.multimediaParent {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
|
||||||
|
grid-auto-rows: auto;
|
||||||
|
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multimedia {
|
||||||
|
transition: all 0.5s;
|
||||||
|
opacity: 1;
|
||||||
|
height: 250px;
|
||||||
|
|
||||||
|
.q-img {
|
||||||
|
object-fit: cover;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
object-fit: cover;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multimedia:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button {
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
top: 1%;
|
||||||
|
right: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zindex {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragFile {
|
||||||
|
border: 2px dashed $color-spacer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
This file will be deleted: Este archivo va a ser borrado
|
||||||
|
</i18n>
|
|
@ -6,7 +6,7 @@ import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Paginate from 'src/components/PaginateData.vue';
|
import Paginate from 'src/components/PaginateData.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import TeleportSlot from 'components/ui/TeleportSlot';
|
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||||
import { toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
@ -90,22 +90,38 @@ function hide() {
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.rma.user') }}</q-item-label>
|
<q-item-label caption>{{
|
||||||
<q-item-label>{{ row.worker.user.name }}</q-item-label>
|
t('claim.rma.user')
|
||||||
|
}}</q-item-label>
|
||||||
|
<q-item-label>{{
|
||||||
|
row.worker.user.name
|
||||||
|
}}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item class="q-pa-none">
|
<q-item class="q-pa-none">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label caption>{{ t('claim.rma.created') }}</q-item-label>
|
<q-item-label caption>{{
|
||||||
|
t('claim.rma.created')
|
||||||
|
}}</q-item-label>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
{{ toDate(row.created, { timeStyle: 'medium' }) }}
|
{{
|
||||||
|
toDate(row.created, {
|
||||||
|
timeStyle: 'medium',
|
||||||
|
})
|
||||||
|
}}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-card-actions vertical class="justify-between">
|
<q-card-actions vertical class="justify-between">
|
||||||
<q-btn flat round color="orange" icon="vn:bin" @click="confirmRemove(row.id)">
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
color="orange"
|
||||||
|
icon="vn:bin"
|
||||||
|
@click="confirmRemove(row.id)"
|
||||||
|
>
|
||||||
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
|
@ -124,7 +140,13 @@ function hide() {
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn flat :label="t('globals.no')" color="primary" v-close-popup autofocus />
|
<q-btn
|
||||||
|
flat
|
||||||
|
:label="t('globals.no')"
|
||||||
|
color="primary"
|
||||||
|
v-close-popup
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
<q-btn flat :label="t('globals.yes')" color="primary" @click="remove()" />
|
<q-btn flat :label="t('globals.yes')" color="primary" @click="remove()" />
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
redirect: { name: 'ClaimMain' },
|
redirect: { name: 'ClaimMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['ClaimList', 'ClaimRmaList'],
|
main: ['ClaimList', 'ClaimRmaList'],
|
||||||
card: ['ClaimBasicData', 'ClaimRma'],
|
card: ['ClaimBasicData', 'ClaimRma', 'ClaimPhotos'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -76,6 +76,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Claim/Card/ClaimRma.vue'),
|
component: () => import('src/pages/Claim/Card/ClaimRma.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'ClaimPhotos',
|
||||||
|
path: 'photos',
|
||||||
|
meta: {
|
||||||
|
title: 'photos',
|
||||||
|
icon: 'image',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Claim/Card/ClaimPhoto.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 429 KiB |
|
@ -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--standard > .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--standard > .q-btn__content > .block').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data deleted');
|
||||||
|
});
|
||||||
|
});
|
|
@ -28,7 +28,7 @@
|
||||||
// Imports Quasar Cypress AE predefined commands
|
// Imports Quasar Cypress AE predefined commands
|
||||||
// import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
|
// import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
|
||||||
Cypress.Commands.add('login', (user) => {
|
Cypress.Commands.add('login', (user) => {
|
||||||
cy.visit('/#/login');
|
//cy.visit('/#/login');
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/api/accounts/login',
|
url: '/api/accounts/login',
|
||||||
|
|
|
@ -25,7 +25,9 @@ describe('ClaimDescriptorMenu', () => {
|
||||||
|
|
||||||
await vm.deleteClaim();
|
await vm.deleteClaim();
|
||||||
|
|
||||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining({ type: 'positive' }));
|
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ type: 'positive' })
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
||||||
|
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||||
|
import ClaimPhoto from 'pages/Claim/Card/ClaimPhoto.vue';
|
||||||
|
|
||||||
|
describe('ClaimPhoto', () => {
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
const claimMock = {
|
||||||
|
claimDms: [
|
||||||
|
{
|
||||||
|
dmsFk: 1,
|
||||||
|
dms: {
|
||||||
|
contentType: 'contentType',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
id: '1',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
beforeAll(() => {
|
||||||
|
vm = createWrapper(ClaimPhoto, {
|
||||||
|
global: {
|
||||||
|
stubs: ['FetchData', 'TeleportSlot', 'vue-i18n'],
|
||||||
|
mocks: {
|
||||||
|
fetch: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).vm;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteDms()', () => {
|
||||||
|
it('should delete dms and call quasar notify', async () => {
|
||||||
|
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
|
||||||
|
vi.spyOn(vm.quasar, 'notify');
|
||||||
|
|
||||||
|
await vm.deleteDms(0);
|
||||||
|
|
||||||
|
expect(axios.post).toHaveBeenCalledWith(
|
||||||
|
`ClaimDms/${claimMock.claimDms[0].dmsFk}/removeFile`
|
||||||
|
);
|
||||||
|
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ type: 'positive' })
|
||||||
|
);
|
||||||
|
expect(vm.claimDms).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('viewDeleteDms()', () => {
|
||||||
|
it('should call quasar dialog', async () => {
|
||||||
|
vi.spyOn(vm.quasar, 'dialog');
|
||||||
|
|
||||||
|
await vm.viewDeleteDms(1);
|
||||||
|
|
||||||
|
expect(vm.quasar.dialog).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
componentProps: {
|
||||||
|
message: 'This file will be deleted',
|
||||||
|
icon: 'delete',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setClaimDms()', () => {
|
||||||
|
it('should assign claimDms and client from data', async () => {
|
||||||
|
await vm.setClaimDms(claimMock);
|
||||||
|
|
||||||
|
expect(vm.claimDms).toEqual([
|
||||||
|
{
|
||||||
|
dmsFk: 1,
|
||||||
|
dms: {
|
||||||
|
contentType: 'contentType',
|
||||||
|
},
|
||||||
|
isVideo: false,
|
||||||
|
url: '///api/Claims/1/downloadFile?access_token=',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(vm.client).toEqual(claimMock.client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create()', () => {
|
||||||
|
it('should upload file and call quasar notify', async () => {
|
||||||
|
const files = [{ name: 'firstFile' }];
|
||||||
|
|
||||||
|
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
|
||||||
|
vi.spyOn(vm.quasar, 'notify');
|
||||||
|
vi.spyOn(vm.claimDmsRef, 'fetch');
|
||||||
|
|
||||||
|
await vm.create(files);
|
||||||
|
|
||||||
|
expect(axios.post).toHaveBeenCalledWith(
|
||||||
|
'claims/1/uploadFile',
|
||||||
|
new FormData(),
|
||||||
|
expect.objectContaining({
|
||||||
|
params: expect.objectContaining({ hasFile: false }),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(vm.quasar.notify).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ type: 'positive' })
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(vm.claimDmsRef.fetch).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -17,13 +17,44 @@ const mockPush = vi.fn();
|
||||||
vi.mock('vue-router', () => ({
|
vi.mock('vue-router', () => ({
|
||||||
useRouter: () => ({
|
useRouter: () => ({
|
||||||
push: mockPush,
|
push: mockPush,
|
||||||
currentRoute: { value: 'myCurrentRoute' },
|
currentRoute: {
|
||||||
|
value: {
|
||||||
|
params: {
|
||||||
|
id: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
useRoute: () => ({
|
useRoute: () => ({
|
||||||
matched: [],
|
matched: [],
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
class FormDataMock {
|
||||||
|
append() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
delete() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
get() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
getAll() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
has() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
set() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
forEach() {
|
||||||
|
vi.fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global.FormData = FormDataMock;
|
||||||
|
|
||||||
export function createWrapper(component, options) {
|
export function createWrapper(component, options) {
|
||||||
const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false });
|
const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false });
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue