495 lines
16 KiB
Vue
495 lines
16 KiB
Vue
<script setup>
|
|
import { onMounted, ref, computed } from 'vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { toDate, toCurrency } from 'src/filters';
|
|
import CardSummary from 'components/ui/CardSummary.vue';
|
|
import FetchData from 'components/FetchData.vue';
|
|
import { getUrl } from 'src/composables/getUrl';
|
|
import { useSession } from 'src/composables/useSession';
|
|
import VnLv from 'src/components/ui/VnLv.vue';
|
|
import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
|
|
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
|
import VnTitle from 'src/components/common/VnTitle.vue';
|
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
|
import axios from 'axios';
|
|
import dashIfEmpty from 'src/filters/dashIfEmpty';
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const { t } = useI18n();
|
|
const { getTokenMultimedia } = useSession();
|
|
const token = getTokenMultimedia();
|
|
|
|
const $props = defineProps({
|
|
id: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
});
|
|
|
|
const entityId = computed(() => $props.id || route.params.id);
|
|
const ClaimStates = ref([]);
|
|
const claimUrl = ref();
|
|
const salixUrl = ref();
|
|
const claimDmsRef = ref();
|
|
const claimDmsFilter = ref({
|
|
include: [
|
|
{
|
|
relation: 'dms',
|
|
},
|
|
],
|
|
});
|
|
|
|
onMounted(async () => {
|
|
salixUrl.value = await getUrl('');
|
|
claimUrl.value = salixUrl.value + `claim/${entityId.value}/`;
|
|
});
|
|
|
|
const detailsColumns = ref([
|
|
{
|
|
name: 'item',
|
|
label: 'claim.summary.item',
|
|
field: (row) => row.sale.itemFk,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'landed',
|
|
label: 'claim.summary.landed',
|
|
field: (row) => row.sale.ticket.landed,
|
|
format: (value) => toDate(value),
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'quantity',
|
|
label: 'claim.summary.quantity',
|
|
field: (row) => row.sale.quantity,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'claimed',
|
|
label: 'claim.summary.claimed',
|
|
field: (row) => row.quantity,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'description',
|
|
label: 'globals.description',
|
|
field: (row) => row.sale.concept,
|
|
},
|
|
{
|
|
name: 'price',
|
|
label: 'claim.summary.price',
|
|
field: (row) => row.sale.price,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'discount',
|
|
label: 'claim.summary.discount',
|
|
field: (row) => row.sale.discount,
|
|
format: (value) => `${value} %`,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'total',
|
|
label: 'claim.summary.total',
|
|
field: ({ sale }) =>
|
|
toCurrency(sale.quantity * sale.price * ((100 - sale.discount) / 100)),
|
|
sortable: true,
|
|
},
|
|
]);
|
|
|
|
const STATE_COLOR = {
|
|
pending: 'warning',
|
|
incomplete: 'info',
|
|
resolved: 'positive',
|
|
canceled: 'negative',
|
|
};
|
|
function stateColor(code) {
|
|
return STATE_COLOR[code];
|
|
}
|
|
|
|
const developmentColumns = ref([
|
|
{
|
|
name: 'claimReason',
|
|
label: 'claim.summary.reason',
|
|
field: (row) => row.claimReason.description,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'claimResult',
|
|
label: 'claim.summary.result',
|
|
field: (row) => row.claimResult.description,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'claimResponsible',
|
|
label: 'claim.summary.responsible',
|
|
field: (row) => row.claimResponsible.description,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'worker',
|
|
label: 'claim.summary.worker',
|
|
field: (row) => row.worker?.user.nickname,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'claimRedelivery',
|
|
label: 'claim.summary.redelivery',
|
|
field: (row) => row.claimRedelivery.description,
|
|
sortable: true,
|
|
},
|
|
]);
|
|
const claimDms = ref([]);
|
|
const multimediaDialog = ref();
|
|
const multimediaSlide = ref();
|
|
|
|
async function getClaimDms() {
|
|
claimDmsFilter.value.where = { claimFk: entityId.value };
|
|
await claimDmsRef.value.fetch();
|
|
}
|
|
|
|
function setClaimDms(data) {
|
|
claimDms.value = [];
|
|
data.forEach((media) => {
|
|
claimDms.value.push({
|
|
isVideo: media.dms.contentType == 'video/mp4',
|
|
url: `/api/Claims/${media.dmsFk}/downloadFile?access_token=${token}`,
|
|
dmsFk: media.dmsFk,
|
|
});
|
|
});
|
|
}
|
|
|
|
function openDialog(dmsId) {
|
|
multimediaSlide.value = dmsId;
|
|
multimediaDialog.value = true;
|
|
}
|
|
async function changeState(value) {
|
|
await axios.patch(`Claims/updateClaim/${entityId.value}`, { claimStateFk: value });
|
|
router.go(route.fullPath);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<FetchData
|
|
url="ClaimDms"
|
|
:filter="claimDmsFilter"
|
|
@on-fetch="(data) => setClaimDms(data)"
|
|
ref="claimDmsRef"
|
|
/>
|
|
<FetchData url="ClaimStates" @on-fetch="(data) => (ClaimStates = data)" auto-load />
|
|
<CardSummary
|
|
ref="summary"
|
|
:url="`Claims/${entityId}/getSummary`"
|
|
:entity-id="entityId"
|
|
@on-fetch="getClaimDms"
|
|
data-key="claimSummary"
|
|
>
|
|
<template #header="{ entity: { claim } }">
|
|
{{ claim.id }} - {{ claim.client.name }} ({{ claim.client.id }})
|
|
</template>
|
|
<template #header-right>
|
|
<QBtnDropdown
|
|
side
|
|
top
|
|
color="black"
|
|
text-color="white"
|
|
:label="t('ticket.summary.changeState')"
|
|
>
|
|
<QList>
|
|
<QVirtualScroll
|
|
style="max-height: 300px"
|
|
:items="ClaimStates"
|
|
separator
|
|
v-slot="{ item, index }"
|
|
>
|
|
<QItem
|
|
:key="index"
|
|
dense
|
|
clickable
|
|
v-close-popup
|
|
@click="changeState(item.id)"
|
|
>
|
|
<QItemSection>
|
|
<QItemLabel>{{ item.description }}</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</QVirtualScroll>
|
|
</QList>
|
|
</QBtnDropdown>
|
|
</template>
|
|
<template #body="{ entity: { claim, salesClaimed, developments } }">
|
|
<QCard class="vn-one">
|
|
<VnTitle
|
|
:url="`#/claim/${entityId}/basic-data`"
|
|
:text="t('globals.pageTitles.basicData')"
|
|
/>
|
|
<VnLv
|
|
:label="t('claim.summary.created')"
|
|
:value="toDate(claim.created)"
|
|
/>
|
|
<VnLv :label="t('claim.summary.state')">
|
|
<template #value>
|
|
<QChip :color="stateColor(claim.claimState.code)" dense>
|
|
{{ claim.claimState.description }}
|
|
</QChip>
|
|
</template>
|
|
</VnLv>
|
|
<VnLv :label="t('globals.salesPerson')">
|
|
<template #value>
|
|
<VnUserLink
|
|
:name="claim.client?.salesPersonUser?.name"
|
|
:worker-id="claim.client?.salesPersonFk"
|
|
/>
|
|
</template>
|
|
</VnLv>
|
|
<VnLv :label="t('claim.summary.attendedBy')">
|
|
<template #value>
|
|
<VnUserLink
|
|
:name="claim.worker?.user?.nickname"
|
|
:worker-id="claim.workerFk"
|
|
/>
|
|
</template>
|
|
</VnLv>
|
|
<VnLv :label="t('claim.summary.customer')">
|
|
<template #value>
|
|
<span class="link cursor-pointer">
|
|
{{ claim.client?.name }}
|
|
<CustomerDescriptorProxy :id="claim.clientFk" />
|
|
</span>
|
|
</template>
|
|
</VnLv>
|
|
<VnLv
|
|
:label="t('claim.basicData.pickup')"
|
|
:value="`${dashIfEmpty(claim.pickup)}`"
|
|
/>
|
|
</QCard>
|
|
<QCard class="vn-three">
|
|
<VnTitle
|
|
:url="`#/claim/${entityId}/notes`"
|
|
:text="t('claim.summary.notes')"
|
|
/>
|
|
<ClaimNotes
|
|
:id="entityId"
|
|
:add-note="false"
|
|
style="max-height: 300px"
|
|
order="created ASC"
|
|
/>
|
|
</QCard>
|
|
<QCard class="vn-two" v-if="salesClaimed.length > 0">
|
|
<VnTitle
|
|
:url="`#/claim/${entityId}/lines`"
|
|
:text="t('claim.summary.details')"
|
|
/>
|
|
<QTable
|
|
:columns="detailsColumns"
|
|
:rows="salesClaimed"
|
|
flat
|
|
dense
|
|
:rows-per-page-options="[0]"
|
|
hide-bottom
|
|
>
|
|
<template #header="props">
|
|
<QTr :props="props">
|
|
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
|
{{ t(col.label) }}
|
|
</QTh>
|
|
</QTr>
|
|
</template>
|
|
<template #body="props">
|
|
<QTr :props="props">
|
|
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
|
<span v-if="col.name != 'description'">{{
|
|
t(col.value)
|
|
}}</span>
|
|
<QBtn
|
|
v-if="col.name == 'description'"
|
|
flat
|
|
color="blue"
|
|
>{{ col.value }}</QBtn
|
|
>
|
|
<ItemDescriptorProxy
|
|
v-if="col.name == 'description'"
|
|
:id="props.row.sale.itemFk"
|
|
:sale-fk="props.row.saleFk"
|
|
></ItemDescriptorProxy>
|
|
</QTh>
|
|
</QTr>
|
|
</template>
|
|
</QTable>
|
|
</QCard>
|
|
<QCard class="vn-two" v-if="claimDms.length > 0">
|
|
<VnTitle
|
|
:url="`#/claim/${entityId}/photos`"
|
|
:text="t('claim.summary.photos')"
|
|
/>
|
|
<div class="container">
|
|
<div
|
|
class="multimedia-container"
|
|
v-for="(media, index) of claimDms"
|
|
:key="index"
|
|
>
|
|
<div class="relative-position">
|
|
<QIcon
|
|
name="play_circle"
|
|
color="primary"
|
|
size="xl"
|
|
class="absolute-center zindex"
|
|
v-if="media.isVideo"
|
|
@click.stop="openDialog(media.dmsFk)"
|
|
>
|
|
<QTooltip>Video</QTooltip>
|
|
</QIcon>
|
|
<QCard class="multimedia relative-position">
|
|
<QImg
|
|
:src="media.url"
|
|
class="rounded-borders cursor-pointer fit"
|
|
@click="openDialog(media.dmsFk)"
|
|
v-if="!media.isVideo"
|
|
>
|
|
</QImg>
|
|
<video
|
|
:src="media.url"
|
|
class="rounded-borders cursor-pointer fit"
|
|
muted="muted"
|
|
v-if="media.isVideo"
|
|
@click="openDialog(media.dmsFk)"
|
|
/>
|
|
</QCard>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</QCard>
|
|
<QCard class="vn-two" v-if="developments.length > 0">
|
|
<VnTitle
|
|
:url="claimUrl + 'development'"
|
|
:text="t('claim.summary.development')"
|
|
/>
|
|
<QTable
|
|
:columns="developmentColumns"
|
|
:rows="developments"
|
|
flat
|
|
dense
|
|
:rows-per-page-options="[0]"
|
|
hide-bottom
|
|
>
|
|
<template #header="props">
|
|
<QTr :props="props">
|
|
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
|
{{ t(col.label) }}
|
|
</QTh>
|
|
</QTr>
|
|
</template>
|
|
</QTable>
|
|
</QCard>
|
|
<QCard class="vn-max">
|
|
<VnTitle :url="claimUrl + 'action'" :text="t('claim.summary.actions')" />
|
|
<div id="slider-container" class="q-px-xl q-py-md">
|
|
<QSlider
|
|
v-model="claim.responsibility"
|
|
label
|
|
:label-value="t('claim.summary.responsibility')"
|
|
label-always
|
|
color="var()"
|
|
markers
|
|
:marker-labels="[
|
|
{ value: 1, label: t('claim.summary.company') },
|
|
{ value: 5, label: t('claim.summary.person') },
|
|
]"
|
|
:min="1"
|
|
:max="5"
|
|
readonly
|
|
/>
|
|
</div>
|
|
</QCard>
|
|
<QDialog
|
|
v-model="multimediaDialog"
|
|
transition-show="slide-up"
|
|
transition-hide="slide-down"
|
|
>
|
|
<QToolbar class="absolute zindex close-button">
|
|
<QSpace />
|
|
<QBtn icon="close" color="primary" round dense v-close-popup />
|
|
</QToolbar>
|
|
<QCarousel
|
|
swipeable
|
|
animated
|
|
v-model="multimediaSlide"
|
|
arrows
|
|
class="fit"
|
|
>
|
|
<QCarouselSlide
|
|
v-for="media of claimDms"
|
|
:key="media.dmsFk"
|
|
:name="media.dmsFk"
|
|
>
|
|
<QImg
|
|
: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>
|
|
</QCarouselSlide>
|
|
</QCarousel>
|
|
</QDialog>
|
|
</template>
|
|
</CardSummary>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.q-dialog__inner--minimized > div {
|
|
max-width: 80%;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
gap: 15px;
|
|
}
|
|
.multimedia-container {
|
|
flex: 1 0 21%;
|
|
}
|
|
.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;
|
|
}
|
|
|
|
.close-button {
|
|
top: 1%;
|
|
right: 10%;
|
|
}
|
|
|
|
.zindex {
|
|
z-index: 1;
|
|
}
|
|
|
|
.change-state {
|
|
width: 10%;
|
|
}
|
|
</style>
|