8016-userRole #1677

Open
alexm wants to merge 5 commits from 8016-userRole into dev
9 changed files with 144 additions and 56 deletions

View File

@ -654,7 +654,7 @@ const rowCtrlClickFunction = computed(() => {
:search-url="searchUrl"
:disable-infinite-scroll="isTableMode"
:before-save-fn="removeTextValue"
@save-changes="reload"
@save-changes="reload && emit('saveChanges')"
:has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
:auto-load="hasParams || $attrs['auto-load']"
>

View File

@ -1,12 +1,12 @@
<script setup>
import { onBeforeMount, computed, markRaw } from 'vue';
import { onBeforeMount, computed, markRaw, watch } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize';
import VnSubToolbar from '../ui/VnSubToolbar.vue';
const emit = defineEmits(['onFetch']);
const emit = defineEmits(['onFetch', 'onChange']);
const props = defineProps({
id: { type: Number, required: false, default: null },
@ -68,6 +68,12 @@ function hasRouteParam(params, valueToCheck = ':addressId') {
return Object.values(params).includes(valueToCheck);
}
watch(
() => arrayData?.store?.data,
(data) => {
emit('onChange', data);
},
);
function formatUrl(id) {
const newId = id || entityId.value;
const regex = /\/(\d+)/;

View File

@ -29,6 +29,12 @@ const entity = ref();
emit('onFetch', data);
}
"
@on-change="
(data) => {
entity = data;
emit('onFetch', data);
}
"
/>
<VnDescriptor v-model="entity" v-bind="$attrs">
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">

View File

@ -9,6 +9,7 @@ import filter from './Card/AccountFilter.js';
import VnSection from 'src/components/common/VnSection.vue';
import FetchData from 'src/components/FetchData.vue';
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
import RoleJoin from './Role/RoleJoin';
const { t } = useI18n();
const { viewSummary } = useSummaryDialog();
@ -58,7 +59,7 @@ const columns = computed(() => [
columnField: {
component: null,
},
format: ({ role }, dashIfEmpty) => dashIfEmpty(role?.name),
format: ({ role }, dashIfEmpty) => dashIfEmpty(RoleJoin(role)),
create: true,
},
{
@ -149,12 +150,12 @@ const columns = computed(() => [
:right-search="false"
>
<template #more-create-dialog="{ data }">
<VnInputPassword
:label="t('Password')"
v-model="data.password"
:required="true"
autocomplete="new-password"
/>
<VnInputPassword
:label="t('Password')"
v-model="data.password"
:required="true"
autocomplete="new-password"
/>
</template>
</VnTable>
</template>

View File

@ -1,12 +1,13 @@
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import EntityDescriptor from 'components/ui/EntityDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
import VnImg from 'src/components/ui/VnImg.vue';
import filter from './AccountFilter.js';
import useHasAccount from 'src/composables/useHasAccount.js';
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
import AccountCard from './AccountCard.vue';
import RoleJoin from '../Role/RoleJoin';
const $props = defineProps({ id: { type: Number, default: null } });
@ -20,13 +21,7 @@ onMounted(async () => {
</script>
<template>
<EntityDescriptor
ref="descriptor"
:url="`VnUsers/preview`"
:filter="{ ...filter, where: { id: entityId } }"
data-key="Account"
title="nickname"
>
<CardDescriptor v-bind="$attrs" :id="entityId" :card="AccountCard" module="Account">
<template #menu>
<AccountDescriptorMenu :entity-id="entityId" />
</template>
@ -50,7 +45,7 @@ onMounted(async () => {
</template>
<template #body="{ entity }">
<VnLv :label="$t('account.card.nickname')" :value="entity.name" />
<VnLv :label="$t('account.card.role')" :value="entity.role?.name" />
<VnLv :label="$t('account.card.role')" :value="RoleJoin(entity.role)" />
</template>
<template #actions="{ entity }">
<QCardActions class="q-gutter-x-md">
@ -78,7 +73,7 @@ onMounted(async () => {
</QIcon>
</QCardActions>
</template>
</EntityDescriptor>
</CardDescriptor>
</template>
<style scoped>
.q-item__label {

View File

@ -1,3 +1,8 @@
export default {
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
include: {
relation: 'role',
scope: {
include: [{ relation: 'role', scope: { fields: ['id', 'name'] } }],
},
},
};

View File

@ -1,49 +1,120 @@
<script setup>
import { ref, watch } from 'vue';
import { ref, computed } 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';
import VnTable from 'src/components/VnTable/VnTable.vue';
import { useArrayData } from 'src/composables/useArrayData';
const { t } = useI18n();
const route = useRoute();
const arrayData = useArrayData('Account');
const vnTableRef = ref({});
const rolesOptions = ref([]);
const formModelRef = ref();
watch(
() => route.params.id,
() => formModelRef.value.reset()
);
const roleRoleRef = ref();
const suggestions = ref([]);
const myRoleRoles = ref([]);
const selectedRows = ref([]);
const columns = computed(() => [
{
name: 'roleFk',
label: t('account.card.role'),
component: 'select',
attrs: {
options: rolesOptions,
inputDebounce: 0,
},
format: (row) => rolesOptions.value?.find((role) => role.id === row.roleFk)?.name,
create: true,
columnCreate: {
event: {
'update:modelValue': (role) => suggestionNewRole(role),
},
},
columnFilter: false,
cardVisible: true,
},
{
name: 'hasGrant',
label: t('account.card.privileges.delegate'),
component: 'checkbox',
create: true,
columnFilter: false,
cardVisible: true,
},
]);
const suggestionColumns = computed(() => [
{
align: 'center',
name: 'role',
field: 'role',
label: t('Suggestion for role change'),
format: (row) => rolesOptions.value?.find((role) => role.id === row)?.name,
},
]);
async function suggestionNewRole(role) {
const newRoleRole = await roleRoleRef.value.fetch({ where: { inheritsFrom: role } });
suggestions.value = newRoleRole.filter((value) =>
myRoleRoles.value.includes(value.role),
);
}
async function getMyRoleRole(roles) {
const myRoleRole = await roleRoleRef.value.fetch({
where: { inheritsFrom: { inq: roles.map((role) => role.roleFk) } },
});
myRoleRoles.value = myRoleRole.map((role) => role.role);
}
</script>
<template>
<FetchData url="VnRoles" auto-load @on-fetch="(data) => (rolesOptions = data)" />
<FormModel
ref="formModelRef"
model="AccountPrivileges"
url="VnUsers/preview"
:filter="{ where: { id: route.params.id } }"
:url-create="`VnUsers/${route.params.id}/privileges`"
:id="route.params.id"
<FetchData url="VnRoles" @on-fetch="(data) => (rolesOptions = data)" auto-load />
<FetchData ref="roleRoleRef" url="RoleRoles" />
<VnTable
ref="vnTableRef"
data-key="AccountPrivileges"
:url="`VnUsers/${route.params.id}/role`"
save-url="userRoles/crud"
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
:create="{
urlCreate: 'userRoles/crud',
title: t('Add Privileges'),
onDataSaved: () => {
$refs.vnTableRef.reload();
},
showSaveAndContinueBtn: true,
formInitialData: { hasGrant: false },
mapper: (data) => {
return { creates: [{ ...data, userFk: route.params.id }] };
},
}"
:columns
:right-search="false"
:is-editable="true"
@on-fetch="(data) => getMyRoleRole(data)"
@save-changes="() => arrayData.fetch({})"
auto-load
>
<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 #more-create-dialog="{ data }">
<QTable
v-if="data.roleFk && suggestions.length > 0"
:rows="suggestions"
:columns="suggestionColumns"
/>
</template>
</FormModel>
</VnTable>
</template>
<i18n>
es:
Suggestion for role change: Sugerencia de cambio de rol
Add Privileges: Añadir privilegios
</i18n>

View File

@ -6,6 +6,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
import filter from './AccountFilter.js';
import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import RoleJoin from '../Role/RoleJoin';
const $props = defineProps({ id: { type: Number, default: 0 } });
@ -33,7 +34,7 @@ const entityId = computed(() => $props.id || route.params.id);
/>
</QCardSection>
<VnLv :label="$t('account.card.nickname')" :value="entity.name" />
<VnLv :label="$t('account.card.role')" :value="entity.role?.name" />
<VnLv :label="$t('account.card.role')" :value="RoleJoin(entity.role)" />
</QCard>
</template>
</CardSummary>

View File

@ -0,0 +1,3 @@
export default function (roles) {
return roles.map((r) => r.role.name).join(', ');
}