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" :search-url="searchUrl"
:disable-infinite-scroll="isTableMode" :disable-infinite-scroll="isTableMode"
:before-save-fn="removeTextValue" :before-save-fn="removeTextValue"
@save-changes="reload" @save-changes="reload && emit('saveChanges')"
:has-sub-toolbar="$props.hasSubToolbar ?? isEditable" :has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
:auto-load="hasParams || $attrs['auto-load']" :auto-load="hasParams || $attrs['auto-load']"
> >

View File

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

View File

@ -29,6 +29,12 @@ const entity = ref();
emit('onFetch', data); emit('onFetch', data);
} }
" "
@on-change="
(data) => {
entity = data;
emit('onFetch', data);
}
"
/> />
<VnDescriptor v-model="entity" v-bind="$attrs"> <VnDescriptor v-model="entity" v-bind="$attrs">
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName"> <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 VnSection from 'src/components/common/VnSection.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnInputPassword from 'src/components/common/VnInputPassword.vue'; import VnInputPassword from 'src/components/common/VnInputPassword.vue';
import RoleJoin from './Role/RoleJoin';
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
@ -58,7 +59,7 @@ const columns = computed(() => [
columnField: { columnField: {
component: null, component: null,
}, },
format: ({ role }, dashIfEmpty) => dashIfEmpty(role?.name), format: ({ role }, dashIfEmpty) => dashIfEmpty(RoleJoin(role)),
create: true, create: true,
}, },
{ {
@ -149,12 +150,12 @@ const columns = computed(() => [
:right-search="false" :right-search="false"
> >
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnInputPassword <VnInputPassword
:label="t('Password')" :label="t('Password')"
v-model="data.password" v-model="data.password"
:required="true" :required="true"
autocomplete="new-password" autocomplete="new-password"
/> />
</template> </template>
</VnTable> </VnTable>
</template> </template>

View File

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

View File

@ -1,3 +1,8 @@
export default { 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> <script setup>
import { ref, watch } from 'vue'; import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import { useArrayData } from 'src/composables/useArrayData';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const arrayData = useArrayData('Account');
const vnTableRef = ref({});
const rolesOptions = ref([]); const rolesOptions = ref([]);
const formModelRef = ref(); const roleRoleRef = ref();
watch( const suggestions = ref([]);
() => route.params.id, const myRoleRoles = ref([]);
() => formModelRef.value.reset() 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> </script>
<template> <template>
<FetchData url="VnRoles" auto-load @on-fetch="(data) => (rolesOptions = data)" /> <FetchData url="VnRoles" @on-fetch="(data) => (rolesOptions = data)" auto-load />
<FormModel <FetchData ref="roleRoleRef" url="RoleRoles" />
ref="formModelRef" <VnTable
model="AccountPrivileges" ref="vnTableRef"
url="VnUsers/preview" data-key="AccountPrivileges"
:filter="{ where: { id: route.params.id } }" :url="`VnUsers/${route.params.id}/role`"
:url-create="`VnUsers/${route.params.id}/privileges`" save-url="userRoles/crud"
:id="route.params.id" 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 auto-load
> >
<template #form="{ data }"> <template #more-create-dialog="{ data }">
<div class="q-gutter-y-sm"> <QTable
<QCheckbox v-if="data.roleFk && suggestions.length > 0"
v-model="data.hasGrant" :rows="suggestions"
:label="t('account.card.privileges.delegate')" :columns="suggestionColumns"
/> />
<VnSelect
:label="t('account.card.role')"
v-model="data.roleFk"
:options="rolesOptions"
option-value="id"
option-label="name"
hide-selected
:required="true"
/>
</div>
</template> </template>
</FormModel> </VnTable>
</template> </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 filter from './AccountFilter.js';
import AccountDescriptorMenu from './AccountDescriptorMenu.vue'; import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
import VnTitle from 'src/components/common/VnTitle.vue'; import VnTitle from 'src/components/common/VnTitle.vue';
import RoleJoin from '../Role/RoleJoin';
const $props = defineProps({ id: { type: Number, default: 0 } }); const $props = defineProps({ id: { type: Number, default: 0 } });
@ -33,7 +34,7 @@ const entityId = computed(() => $props.id || route.params.id);
/> />
</QCardSection> </QCardSection>
<VnLv :label="$t('account.card.nickname')" :value="entity.name" /> <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> </QCard>
</template> </template>
</CardSummary> </CardSummary>

View File

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