forked from verdnatura/salix-front
Merge branch 'dev' into 6896-EndOrderMigration
This commit is contained in:
commit
a61196b04d
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "24.26.2",
|
"version": "24.28.1",
|
||||||
"description": "Salix frontend",
|
"description": "Salix frontend",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Verdnatura",
|
"author": "Verdnatura",
|
||||||
|
|
|
@ -246,7 +246,13 @@ function updateAndEmit(evt, val, res) {
|
||||||
emit(evt, state.get(modelValue), res);
|
emit(evt, state.get(modelValue), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ save, isLoading, hasChanges });
|
defineExpose({
|
||||||
|
save,
|
||||||
|
isLoading,
|
||||||
|
hasChanges,
|
||||||
|
reset,
|
||||||
|
fetch,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column items-center full-width">
|
<div class="column items-center full-width">
|
||||||
|
|
|
@ -78,7 +78,6 @@ const inputRules = [
|
||||||
<template v-if="$slots.prepend" #prepend>
|
<template v-if="$slots.prepend" #prepend>
|
||||||
<slot name="prepend" />
|
<slot name="prepend" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
|
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
|
||||||
<QIcon
|
<QIcon
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref, onUnmounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -376,6 +376,10 @@ async function clearFilter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setLogTree();
|
setLogTree();
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stateStore.rightDrawer = false;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
|
|
@ -159,9 +159,9 @@ function existSummary(routes) {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
.label {
|
.label {
|
||||||
color: var(--vn-label-color);
|
color: var(--vn-label-color);
|
||||||
width: 8em;
|
width: 9em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: wrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
|
|
|
@ -67,6 +67,7 @@ async function confirm() {
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection class="row items-center">
|
<QCardSection class="row items-center">
|
||||||
<span v-html="message"></span>
|
<span v-html="message"></span>
|
||||||
|
<slot name="customHTML"></slot>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardActions align="right">
|
<QCardActions align="right">
|
||||||
<QBtn
|
<QBtn
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FormModelPopup from 'components/FormModelPopup.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const newAccountForm = reactive({
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
|
const rolesOptions = ref([]);
|
||||||
|
|
||||||
|
const redirectToAccountBasicData = (_, { id }) => {
|
||||||
|
router.push({ name: 'AccountBasicData', params: { id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="VnRoles"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
|
||||||
|
@on-fetch="(data) => (rolesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModelPopup
|
||||||
|
:title="t('account.card.newUser')"
|
||||||
|
url-create="VnUsers"
|
||||||
|
model="users"
|
||||||
|
:form-initial-data="newAccountForm"
|
||||||
|
@on-data-saved="redirectToAccountBasicData"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data, validate }">
|
||||||
|
<div class="column q-gutter-sm">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.name"
|
||||||
|
:label="t('account.create.name')"
|
||||||
|
:rules="validate('VnUser.name')"
|
||||||
|
/>
|
||||||
|
<VnInput
|
||||||
|
v-model="data.nickname"
|
||||||
|
:label="t('account.create.nickname')"
|
||||||
|
:rules="validate('VnUser.nickname')"
|
||||||
|
/>
|
||||||
|
<VnInput
|
||||||
|
v-model="data.email"
|
||||||
|
:label="t('account.create.email')"
|
||||||
|
type="email"
|
||||||
|
:rules="validate('VnUser.email')"
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('account.create.role')"
|
||||||
|
v-model="data.roleFk"
|
||||||
|
:options="rolesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
map-options
|
||||||
|
hide-selected
|
||||||
|
:rules="validate('VnUser.roleFk')"
|
||||||
|
/>
|
||||||
|
<VnInput
|
||||||
|
v-model="data.password"
|
||||||
|
:label="t('account.create.password')"
|
||||||
|
type="password"
|
||||||
|
:rules="validate('VnUser.password')"
|
||||||
|
/>
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('account.create.active')"
|
||||||
|
v-model="data.active"
|
||||||
|
:toggle-indeterminate="false"
|
||||||
|
:rules="validate('VnUser.active')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
exprBuilder: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const rolesOptions = ref([]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="VnRoles"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
|
||||||
|
@on-fetch="(data) => (rolesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:search-button="true"
|
||||||
|
:hidden-tags="['search']"
|
||||||
|
:redirect="false"
|
||||||
|
>
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`account.card.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params, searchFn }">
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
:label="t('account.card.name')"
|
||||||
|
v-model="params.name"
|
||||||
|
lazy-rules
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
:label="t('account.card.alias')"
|
||||||
|
v-model="params.nickname"
|
||||||
|
lazy-rules
|
||||||
|
is-outlined
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-mb-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('account.card.role')"
|
||||||
|
v-model="params.roleFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
|
:options="rolesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
hide-selected
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
|
@ -1 +1,144 @@
|
||||||
<template>Account list</template>
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
|
import AccountSummary from './Card/AccountSummary.vue';
|
||||||
|
import AccountFilter from './AccountFilter.vue';
|
||||||
|
import AccountCreate from './AccountCreate.vue';
|
||||||
|
|
||||||
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useRole } from 'src/composables/useRole';
|
||||||
|
import { QDialog } from 'quasar';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
const accountCreateDialogRef = ref(null);
|
||||||
|
const showNewUserBtn = computed(() => useRole().hasAny(['itManagement']));
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: ['id', 'nickname', 'name', 'role'],
|
||||||
|
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
|
||||||
|
};
|
||||||
|
|
||||||
|
const exprBuilder = (param, value) => {
|
||||||
|
switch (param) {
|
||||||
|
case 'search':
|
||||||
|
return /^\d+$/.test(value)
|
||||||
|
? { id: value }
|
||||||
|
: {
|
||||||
|
or: [
|
||||||
|
{ name: { like: `%${value}%` } },
|
||||||
|
{ nickname: { like: `%${value}%` } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
case 'name':
|
||||||
|
case 'nickname':
|
||||||
|
return { [param]: { like: `%${value}%` } };
|
||||||
|
case 'roleFk':
|
||||||
|
return { [param]: value };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getApiUrl = () => new URL(window.location).origin;
|
||||||
|
|
||||||
|
const navigate = (event, id) => {
|
||||||
|
if (event.ctrlKey || event.metaKey)
|
||||||
|
return window.open(`${getApiUrl()}/#/account/${id}/summary`);
|
||||||
|
router.push({ path: `/account/${id}` });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreateModal = () => accountCreateDialogRef.value.show();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="AccountList"
|
||||||
|
url="VnUsers/preview"
|
||||||
|
:expr-builder="exprBuilder"
|
||||||
|
:label="t('account.search')"
|
||||||
|
:info="t('account.searchInfo')"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
<Teleport to="#actions-append">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
@click="stateStore.toggleRightDrawer()"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="menu"
|
||||||
|
>
|
||||||
|
<QTooltip bottom anchor="bottom right">
|
||||||
|
{{ t('globals.collapseMenu') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit text-grey-8">
|
||||||
|
<AccountFilter data-key="AccountList" :expr-builder="exprBuilder" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="vn-card-list">
|
||||||
|
<VnPaginate
|
||||||
|
:filter="filter"
|
||||||
|
data-key="AccountList"
|
||||||
|
url="VnUsers/preview"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<CardList
|
||||||
|
v-for="row of rows"
|
||||||
|
:id="row.id"
|
||||||
|
:key="row.id"
|
||||||
|
:title="row.nickname"
|
||||||
|
@click="navigate($event, row.id)"
|
||||||
|
>
|
||||||
|
<template #list-items>
|
||||||
|
<VnLv :label="t('account.card.name')" :value="row.nickname">
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('account.card.nickname')"
|
||||||
|
:value="row.username"
|
||||||
|
>
|
||||||
|
</VnLv>
|
||||||
|
</template>
|
||||||
|
<template #actions>
|
||||||
|
<QBtn
|
||||||
|
:label="t('components.smartCard.openSummary')"
|
||||||
|
@click.stop="viewSummary(row.id, AccountSummary)"
|
||||||
|
color="primary"
|
||||||
|
style="margin-top: 15px"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</CardList>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
<QDialog
|
||||||
|
ref="accountCreateDialogRef"
|
||||||
|
transition-hide="scale"
|
||||||
|
transition-show="scale"
|
||||||
|
>
|
||||||
|
<AccountCreate />
|
||||||
|
</QDialog>
|
||||||
|
<QPageSticky :offset="[20, 20]" v-if="showNewUserBtn">
|
||||||
|
<QBtn @click="openCreateModal" color="primary" fab icon="add" />
|
||||||
|
<QTooltip class="text-no-wrap">
|
||||||
|
{{ t('account.card.newUser') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const formModelRef = ref(null);
|
||||||
|
|
||||||
|
const accountFilter = {
|
||||||
|
where: { id: route.params.id },
|
||||||
|
fields: ['id', 'email', 'nickname', 'name', 'accountStateFk', 'packages', 'pickup'],
|
||||||
|
include: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
() => formModelRef.value.reset()
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FormModel
|
||||||
|
ref="formModelRef"
|
||||||
|
:url="`VnUsers/preview`"
|
||||||
|
:url-update="`VnUsers/${route.params.id}/update-user`"
|
||||||
|
:filter="accountFilter"
|
||||||
|
model="Accounts"
|
||||||
|
auto-load
|
||||||
|
@on-data-saved="formModelRef.fetch()"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<div class="q-gutter-y-sm">
|
||||||
|
<VnInput v-model="data.name" :label="t('account.card.nickname')" />
|
||||||
|
<VnInput v-model="data.nickname" :label="t('account.card.alias')" />
|
||||||
|
<VnInput v-model="data.email" :label="t('account.card.email')" />
|
||||||
|
<VnSelect
|
||||||
|
v-model="data.lang"
|
||||||
|
:options="['es', 'en']"
|
||||||
|
:label="t('account.card.lang')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import VnCard from 'components/common/VnCard.vue';
|
||||||
|
import AccountDescriptor from './AccountDescriptor.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const routeName = computed(() => route.name);
|
||||||
|
const customRouteRedirectName = computed(() => routeName.value);
|
||||||
|
const searchBarDataKeys = {
|
||||||
|
AccountSummary: 'AccountSummary',
|
||||||
|
AccountInheritedRoles: 'AccountInheritedRoles',
|
||||||
|
AccountMailForwarding: 'AccountMailForwarding',
|
||||||
|
AccountMailAlias: 'AccountMailAlias',
|
||||||
|
AccountPrivileges: 'AccountPrivileges',
|
||||||
|
AccountLog: 'AccountLog',
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VnCard
|
||||||
|
data-key="Account"
|
||||||
|
:descriptor="AccountDescriptor"
|
||||||
|
:search-data-key="searchBarDataKeys[routeName]"
|
||||||
|
:search-custom-route-redirect="customRouteRedirectName"
|
||||||
|
:search-redirect="!!customRouteRedirectName"
|
||||||
|
:searchbar-label="t('account.search')"
|
||||||
|
:searchbar-info="t('account.searchInfo')"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,134 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
|
import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getTokenMultimedia } = useSession();
|
||||||
|
const entityId = computed(() => {
|
||||||
|
return $props.id || route.params.id;
|
||||||
|
});
|
||||||
|
const data = ref(useCardDescription());
|
||||||
|
const setData = (entity) => (data.value = useCardDescription(entity.nickname, entity.id));
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
where: { id: entityId },
|
||||||
|
fields: ['id', 'nickname', 'name', 'role'],
|
||||||
|
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
|
||||||
|
};
|
||||||
|
function getAccountAvatar() {
|
||||||
|
const token = getTokenMultimedia();
|
||||||
|
return `/api/Images/user/160x160/${entityId.value}/download?access_token=${token}`;
|
||||||
|
}
|
||||||
|
const hasAccount = ref(false);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
:url="`Accounts/${entityId}/exists`"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (hasAccount = data.exists)"
|
||||||
|
/>
|
||||||
|
<CardDescriptor
|
||||||
|
ref="descriptor"
|
||||||
|
:url="`VnUsers/preview`"
|
||||||
|
:filter="filter"
|
||||||
|
module="Account"
|
||||||
|
@on-fetch="setData"
|
||||||
|
data-key="AccountId"
|
||||||
|
:title="data.title"
|
||||||
|
:subtitle="data.subtitle"
|
||||||
|
>
|
||||||
|
<template #header-extra-action>
|
||||||
|
<QBtn
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
size="md"
|
||||||
|
color="white"
|
||||||
|
icon="face"
|
||||||
|
:to="{ name: 'AccountList' }"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Go to module index') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</template>
|
||||||
|
<template #menu>
|
||||||
|
<AccountDescriptorMenu :has-account="hasAccount" />
|
||||||
|
</template>
|
||||||
|
<template #before>
|
||||||
|
<QImg :src="getAccountAvatar()" class="photo">
|
||||||
|
<template #error>
|
||||||
|
<div
|
||||||
|
class="absolute-full picture text-center q-pa-md flex flex-center"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="text-grey-5" style="opacity: 0.4; font-size: 5vh">
|
||||||
|
<QIcon name="vn:claims" />
|
||||||
|
</div>
|
||||||
|
<div class="text-grey-5" style="opacity: 0.4">
|
||||||
|
{{ t('account.imageNotFound') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QImg>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<VnLv :label="t('account.card.nickname')" :value="entity.nickname" />
|
||||||
|
<VnLv :label="t('account.card.role')" :value="entity.role.name" />
|
||||||
|
</template>
|
||||||
|
<template #actions="{ entity }">
|
||||||
|
<QCardActions class="q-gutter-x-md">
|
||||||
|
<QIcon
|
||||||
|
v-if="!entity.active"
|
||||||
|
color="primary"
|
||||||
|
name="vn:disabled"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
class="fill-icon"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('account.card.deactivated') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon
|
||||||
|
color="primary"
|
||||||
|
name="contact_mail"
|
||||||
|
v-if="entity.hasAccount"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
class="fill-icon"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('account.card.enabled') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</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>
|
|
@ -0,0 +1,187 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { computed, ref, toRefs } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const $props = defineProps({
|
||||||
|
hasAccount: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { hasAccount } = toRefs($props);
|
||||||
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const account = computed(() => useArrayData('AccountId').store.data[0]);
|
||||||
|
account.value.hasAccount = hasAccount.value;
|
||||||
|
const entityId = computed(() => +route.params.id);
|
||||||
|
|
||||||
|
async function updateStatusAccount(active) {
|
||||||
|
if (active) {
|
||||||
|
await axios.post(`Accounts`, { id: entityId.value });
|
||||||
|
} else {
|
||||||
|
await axios.delete(`Accounts/${entityId.value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
account.value.hasAccount = active;
|
||||||
|
const status = active ? 'enable' : 'disable';
|
||||||
|
quasar.notify({
|
||||||
|
message: t(`account.card.${status}Account.success`),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function updateStatusUser(active) {
|
||||||
|
await axios.patch(`VnUsers/${entityId.value}`, { active });
|
||||||
|
account.value.active = active;
|
||||||
|
const status = active ? 'activate' : 'deactivate';
|
||||||
|
quasar.notify({
|
||||||
|
message: t(`account.card.actions.${status}User.success`),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function setPassword() {
|
||||||
|
quasar.dialog({
|
||||||
|
component: CustomerChangePassword,
|
||||||
|
componentProps: {
|
||||||
|
id: entityId.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const showSyncDialog = ref(false);
|
||||||
|
const syncPassword = ref(null);
|
||||||
|
const shouldSyncPassword = ref(false);
|
||||||
|
async function sync() {
|
||||||
|
const params = { force: true };
|
||||||
|
if (shouldSyncPassword.value) params.password = syncPassword.value;
|
||||||
|
await axios.patch(`Accounts/${account.value.name}/sync`, {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
quasar.notify({
|
||||||
|
message: t('account.card.actions.sync.success'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnConfirm
|
||||||
|
v-model="showSyncDialog"
|
||||||
|
:message="t('account.card.actions.sync.message')"
|
||||||
|
:title="t('account.card.actions.sync.title')"
|
||||||
|
:promise="sync"
|
||||||
|
>
|
||||||
|
<template #customHTML>
|
||||||
|
{{ shouldSyncPassword }}
|
||||||
|
<QCheckbox
|
||||||
|
:label="t('account.card.actions.sync.checkbox')"
|
||||||
|
v-model="shouldSyncPassword"
|
||||||
|
class="full-width"
|
||||||
|
clearable
|
||||||
|
clear-icon="close"
|
||||||
|
>
|
||||||
|
<QIcon style="padding-left: 10px" color="primary" name="info" size="sm">
|
||||||
|
<QTooltip>{{ t('account.card.actions.sync.tooltip') }}</QTooltip>
|
||||||
|
</QIcon></QCheckbox
|
||||||
|
>
|
||||||
|
<QInput
|
||||||
|
v-if="shouldSyncPassword"
|
||||||
|
:label="t('login.password')"
|
||||||
|
v-model="syncPassword"
|
||||||
|
class="full-width"
|
||||||
|
clearable
|
||||||
|
clear-icon="close"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnConfirm>
|
||||||
|
<QItem v-ripple clickable @click="setPassword">
|
||||||
|
<QItemSection>{{ t('account.card.actions.setPassword') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem
|
||||||
|
v-if="!account.hasAccount"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('account.card.actions.enableAccount.title'),
|
||||||
|
t('account.card.actions.enableAccount.subtitle'),
|
||||||
|
() => updateStatusAccount(true)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('account.card.actions.enableAccount.name') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem
|
||||||
|
v-if="account.hasAccount"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('account.card.actions.disableAccount.title'),
|
||||||
|
t('account.card.actions.disableAccount.subtitle'),
|
||||||
|
() => updateStatusAccount(false)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('account.card.actions.disableAccount.name') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
|
||||||
|
<QItem
|
||||||
|
v-if="!account.active"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('account.card.actions.activateUser.title'),
|
||||||
|
t('account.card.actions.activateUser.title'),
|
||||||
|
() => updateStatusUser(true)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('account.card.actions.activateUser.name') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem
|
||||||
|
v-if="account.active"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('account.card.actions.deactivateUser.title'),
|
||||||
|
t('account.card.actions.deactivateUser.title'),
|
||||||
|
() => updateStatusUser(false)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('account.card.actions.deactivateUser.name') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-ripple clickable @click="showSyncDialog = true">
|
||||||
|
<QItemSection>{{ t('account.card.actions.sync.name') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
|
||||||
|
<QSeparator />
|
||||||
|
<QItem
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('account.card.actions.delete.title'),
|
||||||
|
t('account.card.actions.delete.subTitle'),
|
||||||
|
removeAccount
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="delete" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('account.card.actions.delete.name') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup>
|
||||||
|
import InheritedRoles from '../InheritedRoles.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<InheritedRoles data-key="AccountInheritedRoles" />
|
||||||
|
</template>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<script setup>
|
||||||
|
import VnLog from 'src/components/common/VnLog.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnLog model="User" />
|
||||||
|
</template>
|
|
@ -0,0 +1,187 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref, watch, onMounted, nextTick } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import AccountMailAliasCreateForm from './AccountMailAliasCreateForm.vue';
|
||||||
|
|
||||||
|
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const paginateRef = ref(null);
|
||||||
|
const createMailAliasDialogRef = ref(null);
|
||||||
|
|
||||||
|
const arrayData = useArrayData('AccountMailAliases');
|
||||||
|
const store = arrayData.store;
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const hasAccount = ref(false);
|
||||||
|
const data = computed(() => {
|
||||||
|
const dataCopy = store.data;
|
||||||
|
return dataCopy.sort((a, b) => a.alias?.alias.localeCompare(b.alias?.alias));
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = computed(() => ({
|
||||||
|
where: { account: route.params.id },
|
||||||
|
include: {
|
||||||
|
relation: 'alias',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'alias', 'description'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const urlPath = 'MailAliasAccounts';
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'action',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const fetchAccountExistence = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(`Accounts/${route.params.id}/exists`);
|
||||||
|
return data.exists;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching account existence', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteMailAlias = async (row) => {
|
||||||
|
try {
|
||||||
|
await axios.delete(`${urlPath}/${row.id}`);
|
||||||
|
fetchMailAliases();
|
||||||
|
notify(t('Unsubscribed from alias!'), 'positive');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMailAlias = async (mailAliasFormData) => {
|
||||||
|
try {
|
||||||
|
await axios.post(urlPath, mailAliasFormData);
|
||||||
|
notify(t('Subscribed to alias!'), 'positive');
|
||||||
|
fetchMailAliases();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMailAliases = async () => {
|
||||||
|
await nextTick();
|
||||||
|
paginateRef.value.fetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAccountData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
hasAccount.value = await fetchAccountExistence();
|
||||||
|
if (!hasAccount.value) {
|
||||||
|
loading.value = false;
|
||||||
|
store.data = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await fetchMailAliases();
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCreateMailAliasForm = () => createMailAliasDialogRef.value.show();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
() => {
|
||||||
|
store.url = urlPath;
|
||||||
|
store.filter = filter.value;
|
||||||
|
getAccountData();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => await getAccountData());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="full-width" style="max-width: 400px">
|
||||||
|
<QSpinner v-if="loading" color="primary" size="md" />
|
||||||
|
<VnPaginate
|
||||||
|
ref="paginateRef"
|
||||||
|
data-key="AccountMailAliases"
|
||||||
|
:filter="filter"
|
||||||
|
:url="urlPath"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QTable
|
||||||
|
v-if="hasAccount && !loading"
|
||||||
|
:rows="data"
|
||||||
|
:columns="columns"
|
||||||
|
hide-header
|
||||||
|
>
|
||||||
|
<template #body="{ row, rowIndex }">
|
||||||
|
<QTr>
|
||||||
|
<QTd>
|
||||||
|
<div class="column">
|
||||||
|
<span>{{ row.alias?.alias }}</span>
|
||||||
|
<span class="color-vn-label">{{
|
||||||
|
row.alias?.description
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
<QTd style="width: 50px !important">
|
||||||
|
<QIcon
|
||||||
|
name="delete"
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
@click.stop.prevent="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('User will be removed from alias'),
|
||||||
|
t('¿Seguro que quieres continuar?'),
|
||||||
|
() => deleteMailAlias(row, rows, rowIndex)
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('globals.delete') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
<h5 v-if="!hasAccount" class="text-center">
|
||||||
|
{{ t('account.mailForwarding.accountNotEnabled') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<QDialog ref="createMailAliasDialogRef">
|
||||||
|
<AccountMailAliasCreateForm @on-submit-create-alias="createMailAlias" />
|
||||||
|
</QDialog>
|
||||||
|
<QPageSticky position="bottom-right" :offset="[18, 18]">
|
||||||
|
<QBtn fab icon="add" color="primary" @click="openCreateMailAliasForm()">
|
||||||
|
<QTooltip>{{ t('warehouses.add') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Unsubscribed from alias!: ¡Desuscrito del alias!
|
||||||
|
Subscribed to alias!: ¡Suscrito al alias!
|
||||||
|
User will be removed from alias: El usuario será borrado del alias
|
||||||
|
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||||
|
</i18n>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FormPopup from 'components/FormPopup.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onSubmitCreateAlias']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const aliasFormData = reactive({
|
||||||
|
mailAlias: null,
|
||||||
|
account: route.params.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const aliasOptions = ref([]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="MailAliases"
|
||||||
|
:filter="{ fields: ['id', 'alias'], order: 'alias ASC' }"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (aliasOptions = data)"
|
||||||
|
/>
|
||||||
|
<FormPopup
|
||||||
|
model="ZoneWarehouse"
|
||||||
|
@on-submit="emit('onSubmitCreateAlias', aliasFormData)"
|
||||||
|
>
|
||||||
|
<template #form-inputs>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelect
|
||||||
|
:label="t('account.card.alias')"
|
||||||
|
v-model="aliasFormData.mailAlias"
|
||||||
|
:options="aliasOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="alias"
|
||||||
|
hide-selected
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormPopup>
|
||||||
|
</template>
|
|
@ -0,0 +1,159 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, watch, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const initialData = ref({});
|
||||||
|
const formData = ref({
|
||||||
|
forwardTo: null,
|
||||||
|
account: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasAccount = ref(false);
|
||||||
|
const hasData = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
const hasDataChanged = computed(
|
||||||
|
() =>
|
||||||
|
formData.value.forwardTo !== initialData.value.forwardTo ||
|
||||||
|
initialData.value.hasData !== hasData.value
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchAccountExistence = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(`Accounts/${route.params.id}/exists`);
|
||||||
|
return data.exists;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching account existence', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMailForwards = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`MailForwards/${route.params.id}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching mail forwards', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteMailForward = async () => {
|
||||||
|
try {
|
||||||
|
await axios.delete(`MailForwards/${route.params.id}`);
|
||||||
|
formData.value.forwardTo = null;
|
||||||
|
initialData.value.forwardTo = null;
|
||||||
|
initialData.value.hasData = hasData.value;
|
||||||
|
notify(t('globals.dataSaved'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting mail forward', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateMailForward = async () => {
|
||||||
|
try {
|
||||||
|
await axios.patch('MailForwards', formData.value);
|
||||||
|
initialData.value = { ...formData.value };
|
||||||
|
initialData.value.hasData = hasData.value;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error creating mail forward', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (hasData.value) await updateMailForward();
|
||||||
|
else await deleteMailForward();
|
||||||
|
};
|
||||||
|
|
||||||
|
const setInitialData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
initialData.value.account = route.params.id;
|
||||||
|
formData.value.account = route.params.id;
|
||||||
|
hasAccount.value = await fetchAccountExistence(route.params.id);
|
||||||
|
if (!hasAccount.value) {
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await fetchMailForwards(route.params.id);
|
||||||
|
const forwardTo = result ? result.forwardTo : null;
|
||||||
|
formData.value.forwardTo = forwardTo;
|
||||||
|
initialData.value.forwardTo = forwardTo;
|
||||||
|
|
||||||
|
initialData.value.hasData = hasAccount.value && !!forwardTo;
|
||||||
|
hasData.value = hasAccount.value && !!forwardTo;
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
() => setInitialData()
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => await setInitialData());
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<QSpinner v-if="loading" color="primary" size="md" />
|
||||||
|
<QForm
|
||||||
|
v-else-if="hasAccount"
|
||||||
|
@submit="onSubmit()"
|
||||||
|
class="full-width"
|
||||||
|
style="max-width: 800px"
|
||||||
|
>
|
||||||
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||||
|
<div>
|
||||||
|
<QBtnGroup push class="q-gutter-x-sm">
|
||||||
|
<slot name="moreActions" />
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
icon="restart_alt"
|
||||||
|
flat
|
||||||
|
@click="reset()"
|
||||||
|
:label="t('globals.reset')"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
@click="onSubmit()"
|
||||||
|
:disable="!hasDataChanged"
|
||||||
|
:label="t('globals.save')"
|
||||||
|
/>
|
||||||
|
</QBtnGroup>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
<QCard class="q-pa-lg">
|
||||||
|
<VnRow class="row q-mb-md">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="hasData"
|
||||||
|
:label="t('account.mailForwarding.enableMailForwarding')"
|
||||||
|
:toggle-indeterminate="false"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow v-if="hasData" class="row q-gutter-md q-mb-md">
|
||||||
|
<VnInput
|
||||||
|
v-model="formData.forwardTo"
|
||||||
|
:label="t('account.mailForwarding.forwardingMail')"
|
||||||
|
:info="t('account.mailForwarding.mailInputInfo')"
|
||||||
|
>
|
||||||
|
</VnInput>
|
||||||
|
</VnRow>
|
||||||
|
</QCard>
|
||||||
|
</QForm>
|
||||||
|
<h5 v-else class="text-center">
|
||||||
|
{{ t('account.mailForwarding.accountNotEnabled') }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const rolesOptions = ref([]);
|
||||||
|
const formModelRef = ref();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="VnRoles"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
|
||||||
|
auto-load
|
||||||
|
@on-fetch="(data) => (rolesOptions = data)"
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
ref="formModelRef"
|
||||||
|
model="AccountPrivileges"
|
||||||
|
:url="`VnUsers/${route.params.id}`"
|
||||||
|
:url-create="`VnUsers/${route.params.id}/privileges`"
|
||||||
|
auto-load
|
||||||
|
@on-data-saved="formModelRef.fetch()"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<div class="q-gutter-y-sm">
|
||||||
|
<QCheckbox
|
||||||
|
v-model="data.hasGrant"
|
||||||
|
:label="t('account.card.privileges.delegate')"
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
:label="t('account.card.role')"
|
||||||
|
v-model="data.roleFk"
|
||||||
|
:options="rolesOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { store } = useArrayData('Account');
|
||||||
|
const account = ref(store.data);
|
||||||
|
|
||||||
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const filter = {
|
||||||
|
where: { id: entityId },
|
||||||
|
fields: ['id', 'nickname', 'name', 'role'],
|
||||||
|
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardSummary
|
||||||
|
ref="AccountSummary"
|
||||||
|
url="VnUsers/preview"
|
||||||
|
:filter="filter"
|
||||||
|
@on-fetch="(data) => (account = data)"
|
||||||
|
>
|
||||||
|
<template #header>{{ account.id }} - {{ account.nickname }}</template>
|
||||||
|
<template #body>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<QCardSection class="q-pa-none">
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'AccountBasicData', params: { id: entityId } }"
|
||||||
|
class="header header-link"
|
||||||
|
>
|
||||||
|
{{ t('globals.pageTitles.basicData') }}
|
||||||
|
<QIcon name="open_in_new" />
|
||||||
|
</router-link>
|
||||||
|
</QCardSection>
|
||||||
|
<VnLv :label="t('account.card.nickname')" :value="account.nickname" />
|
||||||
|
<VnLv :label="t('account.card.role')" :value="account.role.name" />
|
||||||
|
</QCard>
|
||||||
|
</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>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script setup>
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: { type: String, required: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const paginateRef = ref(null);
|
||||||
|
|
||||||
|
const arrayData = useArrayData(props.dataKey);
|
||||||
|
const store = arrayData.store;
|
||||||
|
|
||||||
|
const data = computed(() => {
|
||||||
|
const dataCopy = store.data;
|
||||||
|
return dataCopy.sort((a, b) => a.role?.name.localeCompare(b.role?.name));
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = computed(() => ({
|
||||||
|
where: {
|
||||||
|
prindicpalType: 'USER',
|
||||||
|
principalId: route.params.id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
relation: 'role',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name', 'description'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const urlPath = 'RoleMappings';
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
() => {
|
||||||
|
store.url = urlPath;
|
||||||
|
store.filter = filter.value;
|
||||||
|
store.limit = 0;
|
||||||
|
fetchSubRoles();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchSubRoles = () => paginateRef.value.fetch();
|
||||||
|
|
||||||
|
const redirectToRoleSummary = (id) =>
|
||||||
|
router.push({ name: 'RoleSummary', params: { id } });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="full-width" style="max-width: 400px">
|
||||||
|
<VnPaginate
|
||||||
|
ref="paginateRef"
|
||||||
|
:data-key="dataKey"
|
||||||
|
:filter="filter"
|
||||||
|
:url="urlPath"
|
||||||
|
:limit="0"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body>
|
||||||
|
<QTable :rows="data" :columns="columns" hide-header>
|
||||||
|
<template #body="{ row }">
|
||||||
|
<QTr
|
||||||
|
@click="redirectToRoleSummary(row.role?.id)"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<QTd>
|
||||||
|
<div class="column">
|
||||||
|
<span>{{ row.role?.name }}</span>
|
||||||
|
<span class="color-vn-label">{{
|
||||||
|
row.role?.description
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Role removed. Changes will take a while to fully propagate.: Rol eliminado. Los cambios tardaran un tiempo en propagarse completamente.
|
||||||
|
Role added! Changes will take a while to fully propagate.: ¡Rol añadido! Los cambios tardaran un tiempo en propagarse completamente.
|
||||||
|
El rol va a ser eliminado: Role will be removed
|
||||||
|
¿Seguro que quieres continuar?: Are you sure you want to continue?
|
||||||
|
</i18n>
|
|
@ -15,24 +15,75 @@ account:
|
||||||
privileges: Privileges
|
privileges: Privileges
|
||||||
mailAlias: Mail Alias
|
mailAlias: Mail Alias
|
||||||
mailForwarding: Mail Forwarding
|
mailForwarding: Mail Forwarding
|
||||||
|
accountCreate: New user
|
||||||
aliasUsers: Users
|
aliasUsers: Users
|
||||||
card:
|
card:
|
||||||
name: Name
|
name: Name
|
||||||
nickname: User
|
nickname: User
|
||||||
role: Rol
|
role: Role
|
||||||
email: Email
|
email: Email
|
||||||
alias: Alias
|
alias: Alias
|
||||||
lang: Language
|
lang: Language
|
||||||
|
roleFk: Role
|
||||||
|
newUser: New user
|
||||||
|
ticketTracking: Ticket tracking
|
||||||
|
privileges:
|
||||||
|
delegate: Can delegate privileges
|
||||||
|
enabled: Account enabled!
|
||||||
|
disabled: Account disabled!
|
||||||
|
willActivated: User will activated
|
||||||
|
willDeactivated: User will be deactivated
|
||||||
|
activated: User activated!
|
||||||
|
deactivated: User deactivated!
|
||||||
actions:
|
actions:
|
||||||
setPassword: Set password
|
setPassword: Set password
|
||||||
disableAccount:
|
disableAccount:
|
||||||
name: Disable account
|
name: Disable account
|
||||||
title: La cuenta será deshabilitada
|
title: The account will be disabled
|
||||||
subtitle: ¿Seguro que quieres continuar?
|
subtitle: Are you sure you want to continue?
|
||||||
disableUser: Disable user
|
success: 'Account disabled!'
|
||||||
sync: Sync
|
enableAccount:
|
||||||
delete: Delete
|
name: Enable account
|
||||||
|
title: The account will be enabled
|
||||||
|
subtitle: Are you sure you want to continue?
|
||||||
|
success: 'Account enabled!'
|
||||||
|
deactivateUser:
|
||||||
|
name: Deactivate user
|
||||||
|
title: The user will be deactivated
|
||||||
|
subtitle: Are you sure you want to continue?
|
||||||
|
success: 'User deactivated!'
|
||||||
|
activateUser:
|
||||||
|
name: Activate user
|
||||||
|
title: The user will be disabled
|
||||||
|
subtitle: Are you sure you want to continue?
|
||||||
|
success: 'User activated!'
|
||||||
|
sync:
|
||||||
|
name: Sync
|
||||||
|
title: The account will be sync
|
||||||
|
subtitle: Are you sure you want to continue?
|
||||||
|
success: 'User synchronized!'
|
||||||
|
checkbox: Synchronize password
|
||||||
|
message: Do you want to synchronize user?
|
||||||
|
tooltip: If password is not specified, just user attributes are synchronized
|
||||||
|
delete:
|
||||||
|
name: Delete
|
||||||
|
title: The account will be deleted
|
||||||
|
subtitle: Are you sure you want to continue?
|
||||||
|
success: ''
|
||||||
search: Search user
|
search: Search user
|
||||||
|
searchInfo: You can search by id, name or nickname
|
||||||
|
create:
|
||||||
|
name: Name
|
||||||
|
nickname: Nickname
|
||||||
|
email: Email
|
||||||
|
role: Role
|
||||||
|
password: Password
|
||||||
|
active: Active
|
||||||
|
mailForwarding:
|
||||||
|
forwardingMail: Forward email
|
||||||
|
accountNotEnabled: Account not enabled
|
||||||
|
enableMailForwarding: Enable mail forwarding
|
||||||
|
mailInputInfo: All emails will be forwarded to the specified address.
|
||||||
role:
|
role:
|
||||||
pageTitles:
|
pageTitles:
|
||||||
inheritedRoles: Inherited Roles
|
inheritedRoles: Inherited Roles
|
||||||
|
|
|
@ -15,6 +15,7 @@ account:
|
||||||
privileges: Privilegios
|
privileges: Privilegios
|
||||||
mailAlias: Alias de correo
|
mailAlias: Alias de correo
|
||||||
mailForwarding: Reenvío de correo
|
mailForwarding: Reenvío de correo
|
||||||
|
accountCreate: Nuevo usuario
|
||||||
aliasUsers: Usuarios
|
aliasUsers: Usuarios
|
||||||
card:
|
card:
|
||||||
nickname: Usuario
|
nickname: Usuario
|
||||||
|
@ -22,27 +23,66 @@ account:
|
||||||
role: Rol
|
role: Rol
|
||||||
email: Mail
|
email: Mail
|
||||||
alias: Alias
|
alias: Alias
|
||||||
lang: dioma
|
lang: Idioma
|
||||||
|
roleFk: Rol
|
||||||
|
enabled: ¡Cuenta habilitada!
|
||||||
|
disabled: ¡Cuenta deshabilitada!
|
||||||
|
willActivated: El usuario será activado
|
||||||
|
willDeactivated: El usuario será desactivado
|
||||||
|
activated: ¡Usuario activado!
|
||||||
|
deactivated: ¡Usuario desactivado!
|
||||||
|
newUser: Nuevo usuario
|
||||||
|
privileges:
|
||||||
|
delegate: Puede delegar privilegios
|
||||||
actions:
|
actions:
|
||||||
setPassword: Establecer contraseña
|
setPassword: Establecer contraseña
|
||||||
disableAccount:
|
disableAccount:
|
||||||
name: Deshabilitar cuenta
|
name: Deshabilitar cuenta
|
||||||
title: La cuenta será deshabilitada
|
title: La cuenta será deshabilitada
|
||||||
subtitle: ¿Seguro que quieres continuar?
|
subtitle: ¿Seguro que quieres continuar?
|
||||||
disableUser:
|
success: '¡Cuenta deshabilitada!'
|
||||||
|
enableAccount:
|
||||||
|
name: Habilitar cuenta
|
||||||
|
title: La cuenta será habilitada
|
||||||
|
subtitle: ¿Seguro que quieres continuar?
|
||||||
|
success: '¡Cuenta habilitada!'
|
||||||
|
deactivateUser:
|
||||||
name: Desactivar usuario
|
name: Desactivar usuario
|
||||||
title: El usuario será deshabilitado
|
title: El usuario será deshabilitado
|
||||||
subtitle: ¿Seguro que quieres continuar?
|
subtitle: ¿Seguro que quieres continuar?
|
||||||
|
success: '¡Usuario desactivado!'
|
||||||
|
activateUser:
|
||||||
|
name: Activar usuario
|
||||||
|
title: El usuario será activado
|
||||||
|
subtitle: ¿Seguro que quieres continuar?
|
||||||
|
success: '¡Usuario activado!'
|
||||||
sync:
|
sync:
|
||||||
name: Sincronizar
|
name: Sincronizar
|
||||||
title: El usuario será sincronizado
|
title: El usuario será sincronizado
|
||||||
subtitle: ¿Seguro que quieres continuar?
|
subtitle: ¿Seguro que quieres continuar?
|
||||||
|
success: '¡Usuario sincronizado!'
|
||||||
|
checkbox: Sincronizar contraseña
|
||||||
|
message: ¿Quieres sincronizar el usuario?
|
||||||
|
tooltip: Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
|
||||||
delete:
|
delete:
|
||||||
name: Eliminar
|
name: Eliminar
|
||||||
title: El usuario será eliminado
|
title: El usuario será eliminado
|
||||||
subtitle: ¿Seguro que quieres continuar?
|
subtitle: ¿Seguro que quieres continuar?
|
||||||
|
success: ''
|
||||||
search: Buscar usuario
|
search: Buscar usuario
|
||||||
|
searchInfo: Puedes buscar por id, nombre o usuario
|
||||||
|
create:
|
||||||
|
name: Nombre
|
||||||
|
nickname: Nombre mostrado
|
||||||
|
email: Email
|
||||||
|
role: Rol
|
||||||
|
password: Contraseña
|
||||||
|
active: Activo
|
||||||
|
mailForwarding:
|
||||||
|
forwardingMail: Dirección de reenvío
|
||||||
|
accountNotEnabled: Cuenta no habilitada
|
||||||
|
enableMailForwarding: Habilitar redirección de correo
|
||||||
|
mailInputInfo: Todos los correos serán reenviados a la dirección especificada, no se mantendrá copia de los mismos en el buzón del usuario.
|
||||||
role:
|
role:
|
||||||
pageTitles:
|
pageTitles:
|
||||||
inheritedRoles: Roles heredados
|
inheritedRoles: Roles heredados
|
||||||
|
|
|
@ -143,10 +143,6 @@ function handleLocation(data, location) {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
||||||
<VnRow>
|
<VnRow>
|
||||||
<QCheckbox
|
|
||||||
:label="t('Incoterms authorization')"
|
|
||||||
v-model="data.hasIncoterms"
|
|
||||||
/>
|
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
:label="t('Electronic invoice')"
|
:label="t('Electronic invoice')"
|
||||||
v-model="data.hasElectronicInvoice"
|
v-model="data.hasElectronicInvoice"
|
||||||
|
|
|
@ -306,10 +306,8 @@ const creditWarning = computed(() => {
|
||||||
:value="entity.recommendedCredit"
|
:value="entity.recommendedCredit"
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard>
|
<QCard class="vn-one">
|
||||||
<div class="header">
|
<VnTitle :text="t('Latest tickets')" />
|
||||||
{{ t('Latest tickets') }}
|
|
||||||
</div>
|
|
||||||
<CustomerSummaryTable />
|
<CustomerSummaryTable />
|
||||||
</QCard>
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -28,7 +28,6 @@ const isLoading = ref(false);
|
||||||
const name = ref(null);
|
const name = ref(null);
|
||||||
const usersPreviewRef = ref(null);
|
const usersPreviewRef = ref(null);
|
||||||
const user = ref([]);
|
const user = ref([]);
|
||||||
const userPasswords = ref(0);
|
|
||||||
|
|
||||||
const dataChanges = computed(() => {
|
const dataChanges = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
@ -45,7 +44,6 @@ const showChangePasswordDialog = () => {
|
||||||
component: CustomerChangePassword,
|
component: CustomerChangePassword,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
id: route.params.id,
|
id: route.params.id,
|
||||||
userPasswords: userPasswords.value,
|
|
||||||
promise: usersPreviewRef.value.fetch(),
|
promise: usersPreviewRef.value.fetch(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -97,11 +95,6 @@ const onSubmit = async () => {
|
||||||
@on-fetch="(data) => (canChangePassword = data)"
|
@on-fetch="(data) => (canChangePassword = data)"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (userPasswords = data[0])"
|
|
||||||
auto-load
|
|
||||||
url="UserPasswords"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||||
<QBtnGroup push class="q-gutter-x-sm">
|
<QBtnGroup push class="q-gutter-x-sm">
|
||||||
|
|
|
@ -9,6 +9,7 @@ import useNotify from 'src/composables/useNotify';
|
||||||
|
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
|
|
||||||
const { dialogRef } = useDialogPluginComponent();
|
const { dialogRef } = useDialogPluginComponent();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
@ -19,15 +20,12 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
userPasswords: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
promise: {
|
promise: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const userPasswords = ref({});
|
||||||
|
|
||||||
const closeButton = ref(null);
|
const closeButton = ref(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
@ -60,6 +58,11 @@ const onSubmit = async () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QDialog ref="dialogRef">
|
<QDialog ref="dialogRef">
|
||||||
|
<FetchData
|
||||||
|
@on-fetch="(data) => (userPasswords = data[0])"
|
||||||
|
auto-load
|
||||||
|
url="UserPasswords"
|
||||||
|
/>
|
||||||
<QCard class="q-pa-lg">
|
<QCard class="q-pa-lg">
|
||||||
<QCardSection>
|
<QCardSection>
|
||||||
<QForm @submit.prevent="onSubmit">
|
<QForm @submit.prevent="onSubmit">
|
||||||
|
@ -71,7 +74,7 @@ const onSubmit = async () => {
|
||||||
<QIcon name="close" size="sm" />
|
<QIcon name="close" size="sm" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md" style="flex-direction: column">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnInput
|
<VnInput
|
||||||
:label="t('New password')"
|
:label="t('New password')"
|
||||||
|
@ -84,11 +87,7 @@ const onSubmit = async () => {
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{
|
{{
|
||||||
t('customer.card.passwordRequirements', {
|
t('customer.card.passwordRequirements', {
|
||||||
length: $props.userPasswords.length,
|
...userPasswords,
|
||||||
nAlpha: $props.userPasswords.nAlpha,
|
|
||||||
nDigits: $props.userPasswords.nDigits,
|
|
||||||
nPunct: $props.userPasswords.nPunct,
|
|
||||||
nUpper: $props.userPasswords.nUpper,
|
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
|
|
|
@ -162,6 +162,7 @@ const navigateToticketSummary = (id) => {
|
||||||
params: { id },
|
params: { id },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const commonColumns = (col) => ['date', 'state', 'total'].includes(col);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -171,13 +172,13 @@ const navigateToticketSummary = (id) => {
|
||||||
auto-load
|
auto-load
|
||||||
url="Tickets"
|
url="Tickets"
|
||||||
/>
|
/>
|
||||||
|
<QCard class="vn-one q-py-sm flex justify-between">
|
||||||
<QTable
|
<QTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:pagination="{ rowsPerPage: 12 }"
|
:pagination="{ rowsPerPage: 12 }"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
class="full-width q-mt-md"
|
class="full-width"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
v-if="rows?.length"
|
|
||||||
>
|
>
|
||||||
<template #body-cell="props">
|
<template #body-cell="props">
|
||||||
<QTd :props="props" @click="navigateToticketSummary(props.row.id)">
|
<QTd :props="props" @click="navigateToticketSummary(props.row.id)">
|
||||||
|
@ -185,19 +186,19 @@ const navigateToticketSummary = (id) => {
|
||||||
<component
|
<component
|
||||||
:is="tableColumnComponents[props.col.name].component"
|
:is="tableColumnComponents[props.col.name].component"
|
||||||
@click="tableColumnComponents[props.col.name].event(props)"
|
@click="tableColumnComponents[props.col.name].event(props)"
|
||||||
class="rounded-borders q-pa-sm"
|
class="rounded-borders"
|
||||||
v-bind="tableColumnComponents[props.col.name].props(props)"
|
v-bind="tableColumnComponents[props.col.name].props(props)"
|
||||||
>
|
>
|
||||||
<template
|
<template v-if="!commonColumns(props.col.name)">
|
||||||
v-if="
|
<span
|
||||||
props.col.name === 'id' ||
|
:class="{
|
||||||
props.col.name === 'nickname' ||
|
link:
|
||||||
props.col.name === 'agency' ||
|
|
||||||
props.col.name === 'route' ||
|
props.col.name === 'route' ||
|
||||||
props.col.name === 'packages'
|
props.col.name === 'nickname',
|
||||||
"
|
}"
|
||||||
>
|
>
|
||||||
{{ props.value }}
|
{{ props.value }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="props.col.name === 'date'">
|
<template v-if="props.col.name === 'date'">
|
||||||
<QBadge class="q-pa-sm" color="warning">
|
<QBadge class="q-pa-sm" color="warning">
|
||||||
|
@ -232,6 +233,7 @@ const navigateToticketSummary = (id) => {
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { reactive, ref, onBeforeMount } from 'vue';
|
import { ref, onBeforeMount } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
|
|
@ -21,7 +21,14 @@ export default {
|
||||||
'AccountAcls',
|
'AccountAcls',
|
||||||
'AccountConnections',
|
'AccountConnections',
|
||||||
],
|
],
|
||||||
card: [],
|
card: [
|
||||||
|
'AccountBasicData',
|
||||||
|
'AccountInheritedRoles',
|
||||||
|
'AccountMailForwarding',
|
||||||
|
'AccountMailAlias',
|
||||||
|
'AccountPrivileges',
|
||||||
|
'AccountLog',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -112,5 +119,81 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountCard',
|
||||||
|
path: ':id',
|
||||||
|
component: () => import('src/pages/Account/Card/AccountCard.vue'),
|
||||||
|
redirect: { name: 'AccountSummary' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'AccountSummary',
|
||||||
|
path: 'summary',
|
||||||
|
meta: {
|
||||||
|
title: 'summary',
|
||||||
|
icon: 'launch',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/Card/AccountSummary.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountBasicData',
|
||||||
|
path: 'basic-data',
|
||||||
|
meta: {
|
||||||
|
title: 'basicData',
|
||||||
|
icon: 'vn:settings',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Account/Card/AccountBasicData.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountInheritedRoles',
|
||||||
|
path: 'inherited-roles',
|
||||||
|
meta: {
|
||||||
|
title: 'inheritedRoles',
|
||||||
|
icon: 'group',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Account/Card/AccountInheritedRoles.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountMailForwarding',
|
||||||
|
path: 'mail-forwarding',
|
||||||
|
meta: {
|
||||||
|
title: 'mailForwarding',
|
||||||
|
icon: 'forward',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Account/Card/AccountMailForwarding.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountMailAlias',
|
||||||
|
path: 'mail-alias',
|
||||||
|
meta: {
|
||||||
|
title: 'mailAlias',
|
||||||
|
icon: 'email',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Account/Card/AccountMailAlias.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountPrivileges',
|
||||||
|
path: 'privileges',
|
||||||
|
meta: {
|
||||||
|
title: 'privileges',
|
||||||
|
icon: 'badge',
|
||||||
|
},
|
||||||
|
component: () =>
|
||||||
|
import('src/pages/Account/Card/AccountPrivileges.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'AccountLog',
|
||||||
|
path: 'log',
|
||||||
|
meta: {
|
||||||
|
title: 'log',
|
||||||
|
icon: 'history',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/Card/AccountLog.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,6 @@ describe('WorkerPda', () => {
|
||||||
it('assign pda', () => {
|
it('assign pda', () => {
|
||||||
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
|
cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
|
||||||
cy.get(deviceProductionField).type('{downArrow}{enter}');
|
cy.get(deviceProductionField).type('{downArrow}{enter}');
|
||||||
cy.get('.vn-row > #simSerialNumber').type('123{enter}');
|
|
||||||
cy.get('.q-notification__message').should('have.text', 'Data created');
|
cy.get('.q-notification__message').should('have.text', 'Data created');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue