Merge branch 'dev' into 7545-hasIncoterms
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
This commit is contained in:
commit
42807d6785
|
@ -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",
|
||||||
|
|
|
@ -87,7 +87,7 @@ const $props = defineProps({
|
||||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||||
const modelValue = computed(
|
const modelValue = computed(
|
||||||
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`
|
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`
|
||||||
);
|
).value;
|
||||||
const componentIsRendered = ref(false);
|
const componentIsRendered = ref(false);
|
||||||
const arrayData = useArrayData(modelValue);
|
const arrayData = useArrayData(modelValue);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
@ -119,9 +119,10 @@ onMounted(async () => {
|
||||||
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
|
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
|
||||||
state.set(modelValue, $props.formInitialData);
|
state.set(modelValue, $props.formInitialData);
|
||||||
|
|
||||||
if ($props.autoLoad && !$props.formInitialData && $props.url) await fetch();
|
if (!$props.formInitialData) {
|
||||||
else if (arrayData.store.data) updateAndEmit('onFetch', arrayData.store.data);
|
if ($props.autoLoad && $props.url) await fetch();
|
||||||
|
else if (arrayData.store.data) updateAndEmit('onFetch', arrayData.store.data);
|
||||||
|
}
|
||||||
if ($props.observeFormChanges) {
|
if ($props.observeFormChanges) {
|
||||||
watch(
|
watch(
|
||||||
() => formData.value,
|
() => formData.value,
|
||||||
|
@ -245,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
|
||||||
|
|
|
@ -115,13 +115,13 @@ const emit = defineEmits(['onFetch']);
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
v-if="$slots.menu"
|
||||||
color="white"
|
color="white"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
icon="more_vert"
|
icon="more_vert"
|
||||||
round
|
round
|
||||||
size="md"
|
size="md"
|
||||||
:class="{ invisible: !$slots.menu }"
|
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('components.cardDescriptor.moreOptions') }}
|
{{ t('components.cardDescriptor.moreOptions') }}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -396,6 +396,7 @@ entry:
|
||||||
type: Type
|
type: Type
|
||||||
color: Color
|
color: Color
|
||||||
id: ID
|
id: ID
|
||||||
|
printedStickers: Printed stickers
|
||||||
notes:
|
notes:
|
||||||
observationType: Observation type
|
observationType: Observation type
|
||||||
descriptor:
|
descriptor:
|
||||||
|
@ -420,6 +421,7 @@ entry:
|
||||||
buyingValue: Buying value
|
buyingValue: Buying value
|
||||||
freightValue: Freight value
|
freightValue: Freight value
|
||||||
comissionValue: Commission value
|
comissionValue: Commission value
|
||||||
|
description: Description
|
||||||
packageValue: Package value
|
packageValue: Package value
|
||||||
isIgnored: Is ignored
|
isIgnored: Is ignored
|
||||||
price2: Grouping
|
price2: Grouping
|
||||||
|
@ -582,6 +584,9 @@ claim:
|
||||||
created: Created
|
created: Created
|
||||||
state: State
|
state: State
|
||||||
pickup: Pick up
|
pickup: Pick up
|
||||||
|
null: No
|
||||||
|
agency: Agency
|
||||||
|
delivery: Delivery
|
||||||
photo:
|
photo:
|
||||||
fileDescription: 'Claim id {claimId} from client {clientName} id {clientId}'
|
fileDescription: 'Claim id {claimId} from client {clientName} id {clientId}'
|
||||||
noData: 'There are no images/videos, click here or drag and drop the file'
|
noData: 'There are no images/videos, click here or drag and drop the file'
|
||||||
|
|
|
@ -394,6 +394,7 @@ entry:
|
||||||
type: Tipo
|
type: Tipo
|
||||||
color: Color
|
color: Color
|
||||||
id: ID
|
id: ID
|
||||||
|
printedStickers: Etiquetas impresas
|
||||||
notes:
|
notes:
|
||||||
observationType: Tipo de observación
|
observationType: Tipo de observación
|
||||||
descriptor:
|
descriptor:
|
||||||
|
@ -418,6 +419,7 @@ entry:
|
||||||
buyingValue: Coste
|
buyingValue: Coste
|
||||||
freightValue: Porte
|
freightValue: Porte
|
||||||
comissionValue: Comisión
|
comissionValue: Comisión
|
||||||
|
description: Descripción
|
||||||
packageValue: Embalaje
|
packageValue: Embalaje
|
||||||
isIgnored: Ignorado
|
isIgnored: Ignorado
|
||||||
price2: Grouping
|
price2: Grouping
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,67 +172,68 @@ const navigateToticketSummary = (id) => {
|
||||||
auto-load
|
auto-load
|
||||||
url="Tickets"
|
url="Tickets"
|
||||||
/>
|
/>
|
||||||
<QTable
|
<QCard class="vn-one q-py-sm flex justify-between">
|
||||||
:columns="columns"
|
<QTable
|
||||||
:pagination="{ rowsPerPage: 12 }"
|
:columns="columns"
|
||||||
:rows="rows"
|
:pagination="{ rowsPerPage: 12 }"
|
||||||
class="full-width q-mt-md"
|
:rows="rows"
|
||||||
row-key="id"
|
class="full-width"
|
||||||
v-if="rows?.length"
|
row-key="id"
|
||||||
>
|
>
|
||||||
<template #body-cell="props">
|
<template #body-cell="props">
|
||||||
<QTd :props="props" @click="navigateToticketSummary(props.row.id)">
|
<QTd :props="props" @click="navigateToticketSummary(props.row.id)">
|
||||||
<QTr :props="props" class="cursor-pointer">
|
<QTr :props="props" class="cursor-pointer">
|
||||||
<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
|
|
||||||
v-if="
|
|
||||||
props.col.name === 'id' ||
|
|
||||||
props.col.name === 'nickname' ||
|
|
||||||
props.col.name === 'agency' ||
|
|
||||||
props.col.name === 'route' ||
|
|
||||||
props.col.name === 'packages'
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ props.value }}
|
<template v-if="!commonColumns(props.col.name)">
|
||||||
</template>
|
<span
|
||||||
<template v-if="props.col.name === 'date'">
|
:class="{
|
||||||
<QBadge class="q-pa-sm" color="warning">
|
link:
|
||||||
{{ props.value }}
|
props.col.name === 'route' ||
|
||||||
</QBadge>
|
props.col.name === 'nickname',
|
||||||
</template>
|
}"
|
||||||
<template v-if="props.col.name === 'state'">
|
>
|
||||||
<QBadge :color="setStateColor(props.row)" class="q-pa-sm">
|
{{ props.value }}
|
||||||
{{ props.value }}
|
</span>
|
||||||
</QBadge>
|
</template>
|
||||||
</template>
|
<template v-if="props.col.name === 'date'">
|
||||||
<template v-if="props.col.name === 'total'">
|
<QBadge class="q-pa-sm" color="warning">
|
||||||
<QBadge
|
{{ props.value }}
|
||||||
:color="setTotalPriceColor(props.row)"
|
</QBadge>
|
||||||
class="q-pa-sm"
|
</template>
|
||||||
v-if="setTotalPriceColor(props.row)"
|
<template v-if="props.col.name === 'state'">
|
||||||
>
|
<QBadge :color="setStateColor(props.row)" class="q-pa-sm">
|
||||||
{{ toCurrency(props.value) }}
|
{{ props.value }}
|
||||||
</QBadge>
|
</QBadge>
|
||||||
<div v-else>{{ toCurrency(props.value) }}</div>
|
</template>
|
||||||
</template>
|
<template v-if="props.col.name === 'total'">
|
||||||
<CustomerDescriptorProxy
|
<QBadge
|
||||||
:id="props.row.clientFk"
|
:color="setTotalPriceColor(props.row)"
|
||||||
v-if="props.col.name === 'nickname'"
|
class="q-pa-sm"
|
||||||
/>
|
v-if="setTotalPriceColor(props.row)"
|
||||||
<RouteDescriptorProxy
|
>
|
||||||
:id="props.row.routeFk"
|
{{ toCurrency(props.value) }}
|
||||||
v-if="props.col.name === 'route'"
|
</QBadge>
|
||||||
/>
|
<div v-else>{{ toCurrency(props.value) }}</div>
|
||||||
</component>
|
</template>
|
||||||
</QTr>
|
<CustomerDescriptorProxy
|
||||||
</QTd>
|
:id="props.row.clientFk"
|
||||||
</template>
|
v-if="props.col.name === 'nickname'"
|
||||||
</QTable>
|
/>
|
||||||
|
<RouteDescriptorProxy
|
||||||
|
:id="props.row.routeFk"
|
||||||
|
v-if="props.col.name === 'route'"
|
||||||
|
/>
|
||||||
|
</component>
|
||||||
|
</QTr>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
|
|
|
@ -82,6 +82,16 @@ const tableColumnComponents = computed(() => ({
|
||||||
},
|
},
|
||||||
event: getInputEvents,
|
event: getInputEvents,
|
||||||
},
|
},
|
||||||
|
printedStickers: {
|
||||||
|
component: VnInput,
|
||||||
|
props: {
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
class: 'input-number',
|
||||||
|
dense: true,
|
||||||
|
},
|
||||||
|
event: getInputEvents,
|
||||||
|
},
|
||||||
weight: {
|
weight: {
|
||||||
component: VnInput,
|
component: VnInput,
|
||||||
props: {
|
props: {
|
||||||
|
@ -147,7 +157,7 @@ const entriesTableColumns = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: t('entry.summary.item'),
|
label: t('entry.summary.item'),
|
||||||
field: 'id',
|
field: 'itemFk',
|
||||||
name: 'item',
|
name: 'item',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
|
@ -169,6 +179,12 @@ const entriesTableColumns = computed(() => {
|
||||||
name: 'stickers',
|
name: 'stickers',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('entry.buys.printedStickers'),
|
||||||
|
field: 'printedStickers',
|
||||||
|
name: 'printedStickers',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('entry.summary.weight'),
|
label: t('entry.summary.weight'),
|
||||||
field: 'weight',
|
field: 'weight',
|
||||||
|
@ -216,7 +232,6 @@ const entriesTableColumns = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const copyOriginalRowsData = (rows) => {
|
const copyOriginalRowsData = (rows) => {
|
||||||
// el objetivo de esto es guardar los valores iniciales de todas las rows para evitar guardar cambios si la data no cambió al disparar los eventos
|
|
||||||
originalRowDataCopy.value = JSON.parse(JSON.stringify(rows));
|
originalRowDataCopy.value = JSON.parse(JSON.stringify(rows));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -386,19 +401,16 @@ const lockIconType = (groupingMode, mode) => {
|
||||||
</template>
|
</template>
|
||||||
<ItemDescriptorProxy
|
<ItemDescriptorProxy
|
||||||
v-if="col.name === 'item'"
|
v-if="col.name === 'item'"
|
||||||
:id="props.row.id"
|
:id="props.row.item.id"
|
||||||
/>
|
/>
|
||||||
</component>
|
</component>
|
||||||
</QTd>
|
</QTd>
|
||||||
</QTr>
|
</QTr>
|
||||||
<QTr no-hover>
|
<QTr no-hover class="full-width infoRow" style="column-span: all">
|
||||||
<QTd />
|
<QTd />
|
||||||
<QTd>
|
<QTd cols>
|
||||||
<span>{{ props.row.item.itemType.code }}</span>
|
<span>{{ props.row.item.itemType.code }}</span>
|
||||||
</QTd>
|
</QTd>
|
||||||
<QTd>
|
|
||||||
<span>{{ props.row.item.id }}</span>
|
|
||||||
</QTd>
|
|
||||||
<QTd>
|
<QTd>
|
||||||
<span>{{ props.row.item.size }}</span>
|
<span>{{ props.row.item.size }}</span>
|
||||||
</QTd>
|
</QTd>
|
||||||
|
@ -413,10 +425,6 @@ const lockIconType = (groupingMode, mode) => {
|
||||||
<FetchedTags :item="props.row.item" :max-length="5" />
|
<FetchedTags :item="props.row.item" :max-length="5" />
|
||||||
</QTd>
|
</QTd>
|
||||||
</QTr>
|
</QTr>
|
||||||
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
|
|
||||||
<QTr v-if="props.rowIndex !== rows.length - 1" class="separation-row">
|
|
||||||
<QTd colspan="12" class="vn-table-separation-row" />
|
|
||||||
</QTr>
|
|
||||||
</template>
|
</template>
|
||||||
<template #item="props">
|
<template #item="props">
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
|
@ -466,11 +474,13 @@ const lockIconType = (groupingMode, mode) => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.separation-row {
|
.q-table--horizontal-separator tbody tr:nth-child(odd) > td {
|
||||||
background-color: var(--vn-section-color) !important;
|
border-bottom-width: 0px;
|
||||||
|
border-top-width: 2px;
|
||||||
|
border-color: var(--vn-text-color);
|
||||||
}
|
}
|
||||||
.grid-style-transition {
|
.infoRow > td {
|
||||||
transition: transform 0.28s, background-color 0.28s;
|
color: var(--vn-label-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -135,14 +135,19 @@ watch;
|
||||||
<template #icons>
|
<template #icons>
|
||||||
<QCardActions class="q-gutter-x-md">
|
<QCardActions class="q-gutter-x-md">
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="currentEntry.isExcludedFromAvailable"
|
v-if="currentEntry?.isExcludedFromAvailable"
|
||||||
name="vn:inventory"
|
name="vn:inventory"
|
||||||
color="primary"
|
color="primary"
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
<QTooltip>{{ t('Inventory entry') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<QIcon v-if="currentEntry.isRaid" name="vn:net" color="primary" size="xs">
|
<QIcon
|
||||||
|
v-if="currentEntry?.isRaid"
|
||||||
|
name="vn:net"
|
||||||
|
color="primary"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
<QTooltip>{{ t('Virtual entry') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</QCardActions>
|
</QCardActions>
|
||||||
|
|
|
@ -167,7 +167,7 @@ const columns = computed(() => [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.description'),
|
label: t('entry.latestBuys.description'),
|
||||||
field: 'description',
|
field: 'description',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -653,6 +653,15 @@ onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
<EntryLatestBuysFilter data-key="EntryLatestBuys" />
|
<EntryLatestBuysFilter data-key="EntryLatestBuys" />
|
||||||
</template>
|
</template>
|
||||||
</RightMenu>
|
</RightMenu>
|
||||||
|
<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>
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
<QTable
|
<QTable
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
|
|
|
@ -184,13 +184,6 @@ const suppliersOptions = ref([]);
|
||||||
@click="removeTag(index, params, searchFn)"
|
@click="removeTag(index, params, searchFn)"
|
||||||
/>
|
/>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem class="q-mt-lg">
|
|
||||||
<QIcon
|
|
||||||
name="add_circle"
|
|
||||||
class="filter-icon"
|
|
||||||
@click="tagValues.push({})"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
</template>
|
||||||
</ItemsFilterPanel>
|
</ItemsFilterPanel>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { onBeforeMount, computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Notify } from 'quasar';
|
import { Notify } from 'quasar';
|
||||||
|
import axios from 'axios';
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
import { toDate } from 'filters/index';
|
import { toDate } from 'filters/index';
|
||||||
|
@ -29,7 +30,6 @@ const columns = computed(() => [
|
||||||
field: (row) => row.hasCmrDms,
|
field: (row) => row.hasCmrDms,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
headerStyle: 'padding-left: 35px',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ticketFk',
|
name: 'ticketFk',
|
||||||
|
@ -62,7 +62,6 @@ const columns = computed(() => [
|
||||||
field: (row) => toDate(row.shipped),
|
field: (row) => toDate(row.shipped),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
headerStyle: 'padding-left: 33px',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'warehouseFk',
|
name: 'warehouseFk',
|
||||||
|
@ -77,6 +76,11 @@ const columns = computed(() => [
|
||||||
field: (row) => row.cmrFk,
|
field: (row) => row.cmrFk,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const { data } = await axios.get('Warehouses');
|
||||||
|
warehouses.value = data;
|
||||||
|
});
|
||||||
function getApiUrl() {
|
function getApiUrl() {
|
||||||
return new URL(window.location).origin;
|
return new URL(window.location).origin;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +191,6 @@ function downloadPdfs() {
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.list {
|
.list {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
|
@ -204,4 +207,10 @@ function downloadPdfs() {
|
||||||
#false {
|
#false {
|
||||||
background-color: $negative;
|
background-color: $negative;
|
||||||
}
|
}
|
||||||
|
:deep(.q-table th) {
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
:deep(.q-table th:nth-child(3)) {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -17,6 +17,10 @@ const $props = defineProps({
|
||||||
required: false,
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
summary: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -106,6 +110,7 @@ const getEntryQueryParams = (supplier) => {
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
@on-fetch="setData"
|
@on-fetch="setData"
|
||||||
data-key="supplier"
|
data-key="supplier"
|
||||||
|
:summary="$props.summary"
|
||||||
>
|
>
|
||||||
<template #header-extra-action>
|
<template #header-extra-action>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import SupplierDescriptor from './SupplierDescriptor.vue';
|
import SupplierDescriptor from './SupplierDescriptor.vue';
|
||||||
|
import SupplierSummary from './SupplierSummary.vue';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -11,6 +12,6 @@ const $props = defineProps({
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QPopupProxy>
|
<QPopupProxy>
|
||||||
<SupplierDescriptor v-if="$props.id" :id="$props.id" />
|
<SupplierDescriptor v-if="$props.id" :id="$props.id" :summary="SupplierSummary" />
|
||||||
</QPopupProxy>
|
</QPopupProxy>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -24,7 +24,7 @@ const agenciesOptions = ref([]);
|
||||||
<FormModel
|
<FormModel
|
||||||
:url="`Travels/${route.params.id}`"
|
:url="`Travels/${route.params.id}`"
|
||||||
:url-update="`Travels/${route.params.id}`"
|
:url-update="`Travels/${route.params.id}`"
|
||||||
model="travel"
|
model="Travel"
|
||||||
auto-load
|
auto-load
|
||||||
>
|
>
|
||||||
<template #form="{ data }">
|
<template #form="{ data }">
|
||||||
|
|
|
@ -1,7 +1,40 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import VnCard from 'components/common/VnCard.vue';
|
import VnCard from 'components/common/VnCard.vue';
|
||||||
import TravelDescriptor from './TravelDescriptor.vue';
|
import TravelDescriptor from './TravelDescriptor.vue';
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'ref',
|
||||||
|
'shipped',
|
||||||
|
'landed',
|
||||||
|
'totalEntries',
|
||||||
|
'warehouseInFk',
|
||||||
|
'warehouseOutFk',
|
||||||
|
'cargoSupplierFk',
|
||||||
|
'agencyModeFk',
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'warehouseIn',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'warehouseOut',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VnCard data-key="Travel" base-url="Travels" :descriptor="TravelDescriptor" />
|
<VnCard
|
||||||
|
data-key="Travel"
|
||||||
|
:filter="filter"
|
||||||
|
base-url="Travels"
|
||||||
|
:descriptor="TravelDescriptor"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
|
import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
|
||||||
|
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
|
||||||
import { toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -52,23 +51,15 @@ const filter = {
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = ref(useCardDescription());
|
|
||||||
|
|
||||||
const setData = (entity) => {
|
|
||||||
data.value = useCardDescription(entity.ref, entity.id);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CardDescriptor
|
<CardDescriptor
|
||||||
module="Travel"
|
module="Travel"
|
||||||
:url="`Travels/${entityId}`"
|
:url="`Travels/${entityId}`"
|
||||||
:title="data.title"
|
title="ref"
|
||||||
:subtitle="data.subtitle"
|
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
@on-fetch="setData"
|
data-key="Travel"
|
||||||
data-key="travelData"
|
|
||||||
>
|
>
|
||||||
<template #header-extra-action>
|
<template #header-extra-action>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
|
|
@ -32,10 +32,11 @@ const cloneTravel = () => {
|
||||||
redirectToCreateView(stringifiedTravelData);
|
redirectToCreateView(stringifiedTravelData);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cloneTravelWithEntries = () => {
|
const cloneTravelWithEntries = async () => {
|
||||||
try {
|
try {
|
||||||
axios.post(`Travels/${$props.travel.id}/cloneWithEntries`);
|
const { data } = await axios.post(`Travels/${$props.travel.id}/cloneWithEntries`);
|
||||||
notify('globals.dataSaved', 'positive');
|
notify('globals.dataSaved', 'positive');
|
||||||
|
router.push({ name: 'TravelBasicData', params: { id: data.id } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.err('Error cloning travel with entries');
|
console.err('Error cloning travel with entries');
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||||
import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
|
import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
|
|
||||||
|
|
||||||
import { toDate, toCurrency } from 'src/filters';
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -222,6 +221,8 @@ async function setTravelData(travelData) {
|
||||||
console.error(`Error setting travel data`, err);
|
console.error(`Error setting travel data`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getLink = (param) => `#/travel/${entityId.value}/${param}`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -240,21 +241,15 @@ async function setTravelData(travelData) {
|
||||||
<template #header>
|
<template #header>
|
||||||
<span>{{ travel.ref }} - {{ travel.id }}</span>
|
<span>{{ travel.ref }} - {{ travel.id }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #header-right>
|
|
||||||
<QBtn color="white" dense flat icon="more_vert" round size="md">
|
|
||||||
<QTooltip>
|
|
||||||
{{ t('components.cardDescriptor.moreOptions') }}
|
|
||||||
</QTooltip>
|
|
||||||
<QMenu>
|
|
||||||
<QList>
|
|
||||||
<TravelDescriptorMenuItems :travel="travel" />
|
|
||||||
</QList>
|
|
||||||
</QMenu>
|
|
||||||
</QBtn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
|
<QCardSection class="q-pa-none">
|
||||||
|
<VnTitle
|
||||||
|
:url="getLink('basic-data')"
|
||||||
|
:text="t('travel.pageTitles.basicData')"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
<VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" />
|
<VnLv :label="t('globals.shipped')" :value="toDate(travel.shipped)" />
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('globals.wareHouseOut')"
|
:label="t('globals.wareHouseOut')"
|
||||||
|
@ -267,6 +262,12 @@ async function setTravelData(travelData) {
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
|
<QCardSection class="q-pa-none">
|
||||||
|
<VnTitle
|
||||||
|
:url="getLink('basic-data')"
|
||||||
|
:text="t('travel.pageTitles.basicData')"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
<VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" />
|
<VnLv :label="t('globals.landed')" :value="toDate(travel.landed)" />
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('globals.wareHouseIn')"
|
:label="t('globals.wareHouseIn')"
|
||||||
|
@ -279,12 +280,18 @@ async function setTravelData(travelData) {
|
||||||
/>
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
|
<QCardSection class="q-pa-none">
|
||||||
|
<VnTitle
|
||||||
|
:url="getLink('basic-data')"
|
||||||
|
:text="t('travel.pageTitles.basicData')"
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" />
|
<VnLv :label="t('globals.agency')" :value="travel.agency?.name" />
|
||||||
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
||||||
<VnLv label="m³" :value="travel.m3" />
|
<VnLv label="m³" :value="travel.m3" />
|
||||||
<VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
|
<VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="full-width" v-if="entriesTableRows.length > 0">
|
<QCard class="full-width">
|
||||||
<VnTitle :text="t('travel.summary.entries')" />
|
<VnTitle :text="t('travel.summary.entries')" />
|
||||||
<QTable
|
<QTable
|
||||||
:rows="entriesTableRows"
|
:rows="entriesTableRows"
|
||||||
|
@ -299,13 +306,15 @@ async function setTravelData(travelData) {
|
||||||
</QTh>
|
</QTh>
|
||||||
</QTr>
|
</QTr>
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-isConfirmed="{ col, value }">
|
<template #body-cell-isConfirmed="{ col, row }">
|
||||||
<QTd>
|
<QTd>
|
||||||
<QIcon
|
<QCheckbox
|
||||||
v-if="col.name === 'isConfirmed'"
|
v-if="col.name === 'isConfirmed'"
|
||||||
:name="value ? 'check' : 'close'"
|
:label="t('travel.summary.received')"
|
||||||
:color="value ? 'positive' : 'negative'"
|
:true-value="1"
|
||||||
size="sm"
|
:false-value="0"
|
||||||
|
v-model="row[col.name]"
|
||||||
|
:disable="true"
|
||||||
/>
|
/>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -53,6 +53,7 @@ const draggedRowIndex = ref(null);
|
||||||
const targetRowIndex = ref(null);
|
const targetRowIndex = ref(null);
|
||||||
const entryRowIndex = ref(null);
|
const entryRowIndex = ref(null);
|
||||||
const draggedEntry = ref(null);
|
const draggedEntry = ref(null);
|
||||||
|
const travelKgPercentages = ref([]);
|
||||||
|
|
||||||
const tableColumnComponents = {
|
const tableColumnComponents = {
|
||||||
id: {
|
id: {
|
||||||
|
@ -88,6 +89,10 @@ const tableColumnComponents = {
|
||||||
component: 'span',
|
component: 'span',
|
||||||
attrs: {},
|
attrs: {},
|
||||||
},
|
},
|
||||||
|
percentage: {
|
||||||
|
component: 'span',
|
||||||
|
attrs: {},
|
||||||
|
},
|
||||||
kg: {
|
kg: {
|
||||||
component: VnInput,
|
component: VnInput,
|
||||||
attrs: { dense: true, type: 'number', min: 0, class: 'input-number' },
|
attrs: { dense: true, type: 'number', min: 0, class: 'input-number' },
|
||||||
|
@ -179,6 +184,14 @@ const columns = computed(() => [
|
||||||
showValue: true,
|
showValue: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '%',
|
||||||
|
field: '',
|
||||||
|
name: 'percentage',
|
||||||
|
align: 'center',
|
||||||
|
showValue: false,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('kg'),
|
label: t('kg'),
|
||||||
field: 'kg',
|
field: 'kg',
|
||||||
|
@ -278,6 +291,8 @@ const saveFieldValue = async (val, field, index) => {
|
||||||
await axios.patch(`Travels/${id}`, params);
|
await axios.patch(`Travels/${id}`, params);
|
||||||
// Actualizar la copia de los datos originales con el nuevo valor
|
// Actualizar la copia de los datos originales con el nuevo valor
|
||||||
originalRowDataCopy.value[index][field] = val;
|
originalRowDataCopy.value[index][field] = val;
|
||||||
|
|
||||||
|
await arrayData.fetch({ append: false });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error updating travel');
|
console.error('Error updating travel');
|
||||||
}
|
}
|
||||||
|
@ -302,6 +317,11 @@ onMounted(async () => {
|
||||||
|
|
||||||
landedTo.value.setDate(landedTo.value.getDate() + 7);
|
landedTo.value.setDate(landedTo.value.getDate() + 7);
|
||||||
landedTo.value.setHours(23, 59, 59, 59);
|
landedTo.value.setHours(23, 59, 59, 59);
|
||||||
|
const { data } = await axios.get('TravelKgPercentages', {
|
||||||
|
params: { filter: JSON.stringify({ order: 'value DESC' }) },
|
||||||
|
});
|
||||||
|
|
||||||
|
travelKgPercentages.value = data;
|
||||||
await getData();
|
await getData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -419,6 +439,11 @@ const handleDragScroll = (event) => {
|
||||||
stopScroll();
|
stopScroll();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getColor = (percentage) => {
|
||||||
|
for (const { value, className } of travelKgPercentages.value)
|
||||||
|
if (percentage > value) return className;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -460,7 +485,7 @@ const handleDragScroll = (event) => {
|
||||||
<template #body="props">
|
<template #body="props">
|
||||||
<QTr
|
<QTr
|
||||||
:props="props"
|
:props="props"
|
||||||
class="cursor-pointer bg-vn-primary-row"
|
class="cursor-pointer bg-travel"
|
||||||
@click="navigateToTravelId(props.row.id)"
|
@click="navigateToTravelId(props.row.id)"
|
||||||
@dragenter="handleDragEnter($event, props.rowIndex)"
|
@dragenter="handleDragEnter($event, props.rowIndex)"
|
||||||
@dragover.prevent
|
@dragover.prevent
|
||||||
|
@ -494,18 +519,32 @@ const handleDragScroll = (event) => {
|
||||||
: {}
|
: {}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<template v-if="col.showValue">
|
<QChip
|
||||||
<span
|
v-if="col.name === 'percentage'"
|
||||||
:class="[
|
:label="
|
||||||
'text-left',
|
props.row.percentageKg
|
||||||
{
|
? `${props.row.percentageKg}%`
|
||||||
'supplier-name':
|
: '-'
|
||||||
col.name === 'cargoSupplierNickname',
|
"
|
||||||
},
|
class="text-left q-py-xs q-px-sm"
|
||||||
]"
|
:color="getColor(props.row.percentageKg)"
|
||||||
>{{ col.value }}</span
|
/>
|
||||||
>
|
<span
|
||||||
</template>
|
v-else-if="col.showValue"
|
||||||
|
:class="[
|
||||||
|
'text-left',
|
||||||
|
{
|
||||||
|
'supplier-name':
|
||||||
|
col.name === 'cargoSupplierNickname',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: ['id', 'cargoSupplierNickname'].includes(
|
||||||
|
col.name
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
v-text="col.value"
|
||||||
|
/>
|
||||||
<!-- Main Row Descriptors -->
|
<!-- Main Row Descriptors -->
|
||||||
<TravelDescriptorProxy
|
<TravelDescriptorProxy
|
||||||
v-if="col.name === 'id'"
|
v-if="col.name === 'id'"
|
||||||
|
@ -539,11 +578,11 @@ const handleDragScroll = (event) => {
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<QTd>
|
<QTd>
|
||||||
<QBtn flat color="primary">{{ entry.id }} </QBtn>
|
<QBtn flat class="link">{{ entry.id }} </QBtn>
|
||||||
<EntryDescriptorProxy :id="entry.id" />
|
<EntryDescriptorProxy :id="entry.id" />
|
||||||
</QTd>
|
</QTd>
|
||||||
<QTd>
|
<QTd>
|
||||||
<QBtn flat color="primary" dense>{{ entry.supplierName }}</QBtn>
|
<QBtn flat class="link" dense>{{ entry.supplierName }}</QBtn>
|
||||||
<SupplierDescriptorProxy :id="entry.supplierFk" />
|
<SupplierDescriptorProxy :id="entry.supplierFk" />
|
||||||
</QTd>
|
</QTd>
|
||||||
<QTd />
|
<QTd />
|
||||||
|
@ -556,6 +595,7 @@ const handleDragScroll = (event) => {
|
||||||
<QTd>
|
<QTd>
|
||||||
<span>{{ entry.stickers }}</span>
|
<span>{{ entry.stickers }}</span>
|
||||||
</QTd>
|
</QTd>
|
||||||
|
<QTd />
|
||||||
<QTd></QTd>
|
<QTd></QTd>
|
||||||
<QTd>
|
<QTd>
|
||||||
<span>{{ entry.loadedkg }}</span>
|
<span>{{ entry.loadedkg }}</span>
|
||||||
|
@ -574,10 +614,23 @@ const handleDragScroll = (event) => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.q-chip {
|
||||||
|
color: var(--vn-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.q-table) {
|
:deep(.q-table) {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.q-td :deep(input) {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-travel {
|
||||||
|
background-color: var(--vn-page-color);
|
||||||
|
border-bottom: 2px solid $primary;
|
||||||
|
}
|
||||||
|
|
||||||
.dashed-border {
|
.dashed-border {
|
||||||
&.--left {
|
&.--left {
|
||||||
border-left: 1px dashed #ccc;
|
border-left: 1px dashed #ccc;
|
||||||
|
|
|
@ -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';
|
||||||
|
@ -15,29 +15,19 @@ const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const newTravelForm = reactive({
|
|
||||||
ref: null,
|
|
||||||
agencyModeFk: null,
|
|
||||||
shipped: null,
|
|
||||||
landed: null,
|
|
||||||
warehouseOutFk: null,
|
|
||||||
warehouseInFk: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const agenciesOptions = ref([]);
|
const agenciesOptions = ref([]);
|
||||||
const warehousesOptions = ref([]);
|
const warehousesOptions = ref([]);
|
||||||
const viewAction = ref();
|
const viewAction = ref();
|
||||||
|
|
||||||
|
const newTravelForm = ref({});
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
// Esto nos permite decirle a FormModel si queremos observar los cambios o no
|
|
||||||
// Ya que si queremos clonar queremos que nos permita guardar inmediatamente sin realizar cambios en el form
|
|
||||||
viewAction.value = route.query.travelData ? 'clone' : 'create';
|
viewAction.value = route.query.travelData ? 'clone' : 'create';
|
||||||
|
|
||||||
if (route.query.travelData) {
|
if (route.query.travelData) {
|
||||||
const travelData = JSON.parse(route.query.travelData);
|
const travelData = JSON.parse(route.query.travelData);
|
||||||
for (let key in newTravelForm) {
|
|
||||||
newTravelForm[key] = travelData[key];
|
newTravelForm.value = { ...newTravelForm.value, ...travelData };
|
||||||
}
|
delete newTravelForm.value.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,8 +50,8 @@ const redirectToTravelBasicData = (_, { id }) => {
|
||||||
<QPage>
|
<QPage>
|
||||||
<VnSubToolbar />
|
<VnSubToolbar />
|
||||||
<FormModel
|
<FormModel
|
||||||
url-update="Travels"
|
url-create="Travels"
|
||||||
model="travel"
|
model="travelCreate"
|
||||||
:form-initial-data="newTravelForm"
|
:form-initial-data="newTravelForm"
|
||||||
:observe-form-changes="viewAction === 'create'"
|
:observe-form-changes="viewAction === 'create'"
|
||||||
@on-data-saved="redirectToTravelBasicData"
|
@on-data-saved="redirectToTravelBasicData"
|
||||||
|
|
|
@ -40,6 +40,7 @@ onMounted(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormModel
|
<FormModel
|
||||||
|
model="createDepartmentChild"
|
||||||
:form-initial-data="departmentChildData"
|
:form-initial-data="departmentChildData"
|
||||||
:observe-form-changes="false"
|
:observe-form-changes="false"
|
||||||
:default-actions="false"
|
:default-actions="false"
|
||||||
|
|
|
@ -6,12 +6,11 @@ import { useQuasar } from 'quasar';
|
||||||
import DepartmentDescriptorProxy from 'src/pages/Department/Card/DepartmentDescriptorProxy.vue';
|
import DepartmentDescriptorProxy from 'src/pages/Department/Card/DepartmentDescriptorProxy.vue';
|
||||||
import CreateDepartmentChild from './CreateDepartmentChild.vue';
|
import CreateDepartmentChild from './CreateDepartmentChild.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { notify } = useNotify();
|
|
||||||
const state = useState();
|
const state = useState();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -62,25 +61,28 @@ const removeNode = (node) => {
|
||||||
const { id, parentFk } = node;
|
const { id, parentFk } = node;
|
||||||
quasar
|
quasar
|
||||||
.dialog({
|
.dialog({
|
||||||
title: t('Are you sure you want to delete it?'),
|
component: VnConfirm,
|
||||||
message: t('Delete department'),
|
componentProps: {
|
||||||
ok: {
|
title: t('Are you sure you want to delete it?'),
|
||||||
push: true,
|
message: t('Delete department'),
|
||||||
color: 'primary',
|
promise: () => remove(id),
|
||||||
},
|
},
|
||||||
cancel: true,
|
|
||||||
})
|
})
|
||||||
.onOk(async () => {
|
.onOk(async () => await fetchNodeLeaves(parentFk));
|
||||||
try {
|
|
||||||
await axios.post(`/Departments/${id}/removeChild`, id);
|
|
||||||
notify(t('department.departmentRemoved'), 'positive');
|
|
||||||
await fetchNodeLeaves(parentFk);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error removing department');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function remove(id) {
|
||||||
|
try {
|
||||||
|
await axios.post(`/Departments/${id}/removeChild`, { id });
|
||||||
|
quasar.notify({
|
||||||
|
message: t('department.departmentRemoved'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error removing department');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const showCreateNodeForm = (nodeId) => {
|
const showCreateNodeForm = (nodeId) => {
|
||||||
showCreateNodeFormVal.value = true;
|
showCreateNodeFormVal.value = true;
|
||||||
creationNodeSelectedId.value = nodeId;
|
creationNodeSelectedId.value = nodeId;
|
||||||
|
|
|
@ -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'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,7 @@ const locationOptions = '[role="listbox"] > div.q-virtual-scroll__content > .q-i
|
||||||
describe('VnLocation', () => {
|
describe('VnLocation', () => {
|
||||||
const dialogInputs = '.q-dialog label input';
|
const dialogInputs = '.q-dialog label input';
|
||||||
describe('Worker Create', () => {
|
describe('Worker Create', () => {
|
||||||
const inputLocation =
|
const inputLocation = '.q-form input[aria-label="Location"]';
|
||||||
'.q-form .q-card > :nth-child(3) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container';
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.viewport(1280, 720);
|
cy.viewport(1280, 720);
|
||||||
cy.login('developer');
|
cy.login('developer');
|
||||||
|
@ -25,9 +24,6 @@ describe('VnLocation', () => {
|
||||||
cy.get(inputLocation).clear();
|
cy.get(inputLocation).clear();
|
||||||
cy.get(inputLocation).type('ecuador');
|
cy.get(inputLocation).type('ecuador');
|
||||||
cy.get(locationOptions).should('have.length.at.least', 1);
|
cy.get(locationOptions).should('have.length.at.least', 1);
|
||||||
cy.get(
|
|
||||||
'.q-form .q-card > :nth-child(3) > .q-field > .q-field__inner > .q-field__control > :nth-child(3) > .q-icon'
|
|
||||||
).click();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('Fiscal-data', () => {
|
describe('Fiscal-data', () => {
|
||||||
|
@ -38,9 +34,7 @@ describe('VnLocation', () => {
|
||||||
cy.waitForElement('.q-form');
|
cy.waitForElement('.q-form');
|
||||||
});
|
});
|
||||||
it('Create postCode', function () {
|
it('Create postCode', function () {
|
||||||
cy.get(
|
cy.get('.q-form > .q-card > .vn-row:nth-child(6) .--add-icon').click();
|
||||||
':nth-child(6) > .q-field > .q-field__inner > .q-field__control > :nth-child(2) > .q-icon'
|
|
||||||
).click();
|
|
||||||
cy.get('.q-card > h1').should('have.text', 'New postcode');
|
cy.get('.q-card > h1').should('have.text', 'New postcode');
|
||||||
cy.get(dialogInputs).eq(0).clear('12');
|
cy.get(dialogInputs).eq(0).clear('12');
|
||||||
cy.get(dialogInputs).eq(0).type('1234453');
|
cy.get(dialogInputs).eq(0).type('1234453');
|
||||||
|
|
|
@ -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