feat: roles sections

This commit is contained in:
Javier Segarra 2024-05-10 22:01:57 +02:00
parent 555b5a663c
commit f9e77596e4
8 changed files with 242 additions and 350 deletions

View File

@ -27,7 +27,7 @@ function stateColor(code) {
function navigate(event, id) {
if (event.ctrlKey || event.metaKey)
return window.open(`${getApiUrl()}/#/account/${id}/summary`);
router.push({ path: `/account/role/${id}` });
router.push({ path: `/role/${id}/summary` });
}
</script>

View File

@ -0,0 +1,6 @@
<script setup>
import VnLog from 'src/components/common/VnLog.vue';
</script>
<template>
<VnLog model="Account"></VnLog>
</template>

View File

@ -0,0 +1,151 @@
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toDate, toPercentage } from 'src/filters';
import { useState } from 'src/composables/useState';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import useCardDescription from 'src/composables/useCardDescription';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { getUrl } from 'src/composables/getUrl';
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const route = useRoute();
const state = useState();
const { t } = useI18n();
const salixUrl = ref();
const entityId = computed(() => {
return $props.id || route.params.id;
});
const filter = {
where: { id: entityId },
fields: ['id', 'nickname', 'name', 'role'],
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
};
</script>
<template>
<CardDescriptor
ref="descriptor"
:url="`VnUsers/preview`"
:filter="filter"
module="Account"
@on-fetch="setData"
data-key="accountData"
>
<template #menu="{ entity }">
{{ entity }}
</template>
<template #body="{ entity }">
<VnLv v-if="entity.accountState" :label="t('account.card.state')">
<template #value>
<QBadge
:color="stateColor(entity.accountState.code)"
text-color="black"
dense
>
{{ entity.accountState.description }}
</QBadge>
</template>
</VnLv>
<VnLv :label="t('account.card.created')" :value="toDate(entity.created)" />
<VnLv :label="t('account.card.commercial')">
<template #value>
<VnUserLink
:name="entity.client?.salesPersonUser?.name"
:worker-id="entity.client?.salesPersonFk"
/>
</template>
</VnLv>
<VnLv
v-if="entity.worker"
:label="t('account.card.attendedBy')"
:value="entity.worker.user.name"
>
<template #value>
<VnUserLink
:name="entity.worker.user.nickname"
:worker-id="entity.worker.id"
/>
</template>
</VnLv>
<VnLv :label="t('account.card.zone')">
<template #value>
<span class="link">
{{ entity.ticket?.zone?.name }}
</span>
</template>
</VnLv>
<VnLv
:label="t('account.card.province')"
:value="entity.ticket?.address?.province?.name"
/>
<VnLv :label="t('account.card.ticketId')">
<template #value>
<span class="link">
{{ entity.ticketFk }}
</span>
</template>
</VnLv>
<VnLv
:label="t('accountRate')"
:value="toPercentage(entity.client?.accountsRatio?.accountingRate)"
/>
</template>
<template #actions="{ entity }">
<QCardActions>
<QBtn
size="md"
icon="vn:client"
color="primary"
:to="{ name: 'CustomerCard', params: { id: entity.clientFk } }"
>
<QTooltip>{{ t('account.card.customerSummary') }}</QTooltip>
</QBtn>
<QBtn
size="md"
icon="vn:ticket"
color="primary"
:to="{ name: 'TicketCard', params: { id: entity.ticketFk } }"
>
<QTooltip>{{ t('account.card.accountedTicket') }}</QTooltip>
</QBtn>
<QBtn
size="md"
icon="assignment"
color="primary"
:href="salixUrl + 'ticket/' + entity.ticketFk + '/sale-tracking'"
>
<QTooltip>{{ t('account.card.saleTracking') }}</QTooltip>
</QBtn>
<QBtn
size="md"
icon="visibility"
color="primary"
:href="salixUrl + 'ticket/' + entity.ticketFk + '/tracking/index'"
>
<QTooltip>{{ t('account.card.ticketTracking') }}</QTooltip>
</QBtn>
</QCardActions>
</template>
</CardDescriptor>
</template>
<style scoped>
.q-item__label {
margin-top: 0;
}
</style>
<i18n>
en:
accountRate: Claming rate
es:
accountRate: Ratio de reclamación
</i18n>

View File

@ -0,0 +1,6 @@
<script setup>
import VnLog from 'src/components/common/VnLog.vue';
</script>
<template>
<VnLog model="Account"></VnLog>
</template>

View File

@ -27,22 +27,8 @@ const $props = defineProps({
});
const entityId = computed(() => $props.id || route.params.id);
const AccountStates = ref([]);
const accountUrl = ref();
const salixUrl = ref();
const accountDmsRef = ref();
const accountDmsFilter = ref({
include: [
{
relation: 'dms',
},
],
});
onMounted(async () => {
salixUrl.value = await getUrl('');
accountUrl.value = salixUrl.value + `account/${entityId.value}/`;
});
onMounted(async () => {});
const detailsColumns = ref([
{
@ -96,347 +82,20 @@ const detailsColumns = ref([
sortable: true,
},
]);
const STATE_COLOR = {
pending: 'warning',
incomplete: 'info',
resolved: 'positive',
canceled: 'negative',
};
function stateColor(code) {
return STATE_COLOR[code];
}
const developmentColumns = ref([
{
name: 'accountReason',
label: 'account.summary.reason',
field: (row) => row.accountReason.description,
sortable: true,
},
{
name: 'accountResult',
label: 'account.summary.result',
field: (row) => row.accountResult.description,
sortable: true,
},
{
name: 'accountResponsible',
label: 'account.summary.responsible',
field: (row) => row.accountResponsible.description,
sortable: true,
},
{
name: 'worker',
label: 'account.summary.worker',
field: (row) => row.worker?.user.nickname,
sortable: true,
},
{
name: 'accountRedelivery',
label: 'account.summary.redelivery',
field: (row) => row.accountRedelivery.description,
sortable: true,
},
]);
const accountDms = ref([]);
const multimediaDialog = ref();
const multimediaSlide = ref();
async function getAccountDms() {
accountDmsFilter.value.where = { accountFk: entityId.value };
await accountDmsRef.value.fetch();
}
function setAccountDms(data) {
accountDms.value = [];
data.forEach((media) => {
accountDms.value.push({
isVideo: media.dms.contentType == 'video/mp4',
url: `/api/Accounts/${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(`Accounts/updateAccount/${entityId.value}`, {
accountStateFk: value,
});
router.go(route.fullPath);
}
</script>
<template>
<FetchData
url="AccountDms"
:filter="accountDmsFilter"
@on-fetch="(data) => setAccountDms(data)"
ref="accountDmsRef"
/>
<FetchData
url="AccountStates"
@on-fetch="(data) => (AccountStates = data)"
auto-load
/>
<CardSummary
ref="summary"
:url="`Accounts/${entityId}/getSummary`"
:url="`VnRoles`"
:entity-id="entityId"
@on-fetch="getAccountDms"
>
<template #header="{ entity: { account } }">
<!-- <template #header="{ entity: { account } }">
{{ account.id }} - {{ account.client.name }} ({{ account.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="AccountStates"
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: { account, salesAccounted, developments } }">
<QCard class="vn-one">
<VnTitle
:url="`#/account/${entityId}/basic-data`"
:text="t('account.pageTitles.basicData')"
/>
<VnLv
:label="t('account.summary.created')"
:value="toDate(account.created)"
/>
<VnLv :label="t('account.summary.state')">
<template #value>
<QChip :color="stateColor(account.accountState.code)" dense>
{{ account.accountState.description }}
</QChip>
</template>
</VnLv>
<VnLv :label="t('globals.salesPerson')">
<template #value>
<VnUserLink
:name="account.client?.salesPersonUser?.name"
:worker-id="account.client?.salesPersonFk"
/>
</template>
</VnLv>
<VnLv :label="t('account.summary.attendedBy')">
<template #value>
<VnUserLink
:name="account.worker?.user?.nickname"
:worker-id="account.workerFk"
/>
</template>
</VnLv>
<VnLv :label="t('account.summary.customer')">
<template #value>
<span class="link cursor-pointer">
{{ account.client?.name }}
</span>
</template>
</VnLv>
<VnLv
:label="t('account.basicData.pickup')"
:value="`${dashIfEmpty(account.pickup)}`"
/>
</QCard>
<QCard class="vn-three">
<VnTitle
:url="`#/account/${entityId}/notes`"
:text="t('account.summary.notes')"
/>
</QCard>
<QCard class="vn-two" v-if="salesAccounted.length > 0">
<VnTitle
:url="`#/account/${entityId}/lines`"
:text="t('account.summary.details')"
/>
<QTable
:columns="detailsColumns"
:rows="salesAccounted"
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
>
</QTh>
</QTr>
</template>
</QTable>
</QCard>
<QCard class="vn-two" v-if="accountDms.length > 0">
<VnTitle
:url="`#/account/${entityId}/photos`"
:text="t('account.summary.photos')"
/>
<div class="container">
<div
class="multimedia-container"
v-for="(media, index) of accountDms"
: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="accountUrl + 'development'"
:text="t('account.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="accountUrl + 'action'"
:text="t('account.summary.actions')"
/>
<div id="slider-container" class="q-px-xl q-py-md">
<QSlider
v-model="account.responsibility"
label
:label-value="t('account.summary.responsibility')"
label-always
color="var()"
markers
:marker-labels="[
{ value: 1, label: t('account.summary.company') },
{ value: 5, label: t('account.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 accountDms"
: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> -->
<template #body="{ entity }">
{{ entity }}
</template>
</CardSummary>
</template>

View File

@ -0,0 +1,6 @@
<script setup>
import VnLog from 'src/components/common/VnLog.vue';
</script>
<template>
<VnLog model="Account"></VnLog>
</template>

View File

@ -53,6 +53,41 @@ export default {
icon: 'group',
},
component: () => import('src/pages/Account/Role/AccountRoles.vue'),
// children: [
// {
// name: 'RoleCard',
// path: ':id',
// component: () =>
// import('src/pages/Account/Role/Card/RoleCard.vue'),
// redirect: { name: 'RoleSummary' },
// children: [
// {
// name: 'RoleSummary',
// path: 'summary',
// meta: {
// title: 'summary',
// icon: 'launch',
// },
// component: () =>
// import(
// 'src/pages/Account/Role/Card/RoleSummary.vue'
// ),
// },
// {
// name: 'RoleBasicData',
// path: 'basic-data',
// meta: {
// title: 'basicData',
// icon: 'vn:settings',
// },
// component: () =>
// import(
// 'src/pages/Account/Role/Card/RoleBasicData.vue'
// ),
// },
// ],
// },
// ],
},
{
path: 'alias',

View File

@ -12,12 +12,12 @@ export default {
redirect: { name: 'RoleCard' },
menus: {
main: [],
card: ['RoleBasicData'],
card: ['RoleBasicData', 'SubRoles', 'InheritedRoles', 'RoleLog'],
},
children: [
{
name: 'RoleCard',
path: ':id',
path: '/role/:id',
component: () => import('src/pages/Account/Role/Card/RoleCard.vue'),
redirect: { name: 'RoleSummary' },
children: [
@ -41,6 +41,35 @@ export default {
component: () =>
import('src/pages/Account/Role/Card/RoleBasicData.vue'),
},
{
name: 'SubRoles',
path: 'sub-roles',
meta: {
title: 'subRoles',
icon: 'group',
},
component: () => import('src/pages/Account/Role/Card/SubRoles.vue'),
},
{
name: 'InheritedRoles',
path: 'inherited-roles',
meta: {
title: 'inheritedRoles',
icon: 'account_tree',
},
component: () =>
import('src/pages/Account/Role/Card/InheritedRoles.vue'),
},
{
name: 'RoleBasicData',
path: 'log',
meta: {
title: 'log',
icon: 'history',
},
component: () => import('src/pages/Account/Role/Card/RoleLog.vue'),
},
],
},
],