mirror of https://github.com/rancher/dashboard.git
217 lines
5.6 KiB
Vue
217 lines
5.6 KiB
Vue
<script>
|
|
import { MANAGEMENT, NORMAN } from '@shell/config/types';
|
|
import ArrayList from '@shell/components/form/ArrayList';
|
|
import Loading from '@shell/components/Loading';
|
|
import { _CREATE, _VIEW } from '@shell/config/query-params';
|
|
import { get, set } from '@shell/utils/object';
|
|
|
|
function normalizeId(id) {
|
|
return id?.replace(':', '/') || id;
|
|
}
|
|
|
|
export function canViewMembershipEditor(store, needsProject = false) {
|
|
return (!!store.getters['management/schemaFor'](MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING) || !needsProject) &&
|
|
!!store.getters['management/schemaFor'](MANAGEMENT.ROLE_TEMPLATE) &&
|
|
!!store.getters['rancher/schemaFor'](NORMAN.PRINCIPAL);
|
|
}
|
|
|
|
export default {
|
|
components: { ArrayList, Loading },
|
|
|
|
props: {
|
|
addMemberDialogName: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
|
|
parentKey: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
|
|
parentId: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
|
|
mode: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
|
|
type: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
|
|
defaultBindingHandler: {
|
|
type: Function,
|
|
default: null,
|
|
},
|
|
|
|
modalSticky: {
|
|
type: Boolean,
|
|
default: false,
|
|
}
|
|
},
|
|
|
|
async fetch() {
|
|
const userHydration = [
|
|
this.schema ? this.$store.dispatch(`rancher/findAll`, { type: this.type, opt: { force: true } }) : [],
|
|
this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL }),
|
|
this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE }),
|
|
this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER })
|
|
];
|
|
const [allBindings] = await Promise.all(userHydration);
|
|
|
|
const bindings = allBindings
|
|
.filter(b => normalizeId(get(b, this.parentKey)) === normalizeId(this.parentId));
|
|
|
|
this.$set(this, 'lastSavedBindings', [...bindings]);
|
|
|
|
// Add the current user as the project owner. This will get created by default
|
|
if (this.mode === _CREATE && bindings.length === 0 && this.defaultBindingHandler) {
|
|
const defaultBinding = await this.defaultBindingHandler();
|
|
|
|
defaultBinding.isDefaultBinding = true;
|
|
bindings.push(defaultBinding);
|
|
}
|
|
|
|
this.$set(this, 'bindings', bindings);
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
schema: this.$store.getters[`rancher/schemaFor`](this.type),
|
|
bindings: [],
|
|
lastSavedBindings: [],
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
newBindings() {
|
|
return this.bindings
|
|
.filter(binding => !binding.id && !this.lastSavedBindings.includes(binding) && !binding.isDefaultBinding);
|
|
},
|
|
removedBindings() {
|
|
return this.lastSavedBindings
|
|
.filter(binding => !this.bindings.includes(binding));
|
|
},
|
|
membershipUpdate() {
|
|
const newBindings = this.newBindings;
|
|
const removedBindings = this.removedBindings;
|
|
|
|
return {
|
|
newBindings: this.newBindings,
|
|
removedBindings: this.removedBindings,
|
|
save: (parentId) => {
|
|
const savedPromises = newBindings.map((binding) => {
|
|
set(binding, this.parentKey, parentId);
|
|
|
|
return binding.save();
|
|
});
|
|
|
|
const removedPromises = removedBindings.map(binding => binding.remove());
|
|
|
|
return Promise.all([...savedPromises, ...removedPromises]);
|
|
}
|
|
};
|
|
},
|
|
|
|
isCreate() {
|
|
return this.mode === _CREATE;
|
|
},
|
|
|
|
isView() {
|
|
return this.mode === _VIEW;
|
|
},
|
|
},
|
|
watch: {
|
|
membershipUpdate: {
|
|
deep: true,
|
|
handler() {
|
|
this.$emit('membership-update', this.membershipUpdate);
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
addMember() {
|
|
this.$store.dispatch('cluster/promptModal', {
|
|
component: this.addMemberDialogName,
|
|
componentProps: { onAdd: this.onAddMember },
|
|
modalSticky: this.modalSticky
|
|
});
|
|
},
|
|
|
|
onAddMember(bindings) {
|
|
this.$set(this, 'bindings', [...this.bindings, ...bindings]);
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
<template>
|
|
<Loading v-if="$fetchState.pending" />
|
|
<ArrayList
|
|
v-else
|
|
v-model="bindings"
|
|
:mode="mode"
|
|
:show-header="true"
|
|
>
|
|
<template #column-headers>
|
|
<div class="box mb-0">
|
|
<div class="column-headers row">
|
|
<div class="col span-6">
|
|
<label class="text-label">{{ t('membershipEditor.user') }}</label>
|
|
</div>
|
|
<div class="col span-6">
|
|
<label class="text-label">{{ t('membershipEditor.role') }}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #columns="{row}">
|
|
<div class="columns row">
|
|
<div class="col span-6">
|
|
<Principal
|
|
:key="row.value.principalId"
|
|
:value="row.value.principalId"
|
|
/>
|
|
</div>
|
|
<div class="col span-6 role">
|
|
{{ row.value.roleDisplay }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #add>
|
|
<button
|
|
type="button"
|
|
class="btn role-primary mt-10"
|
|
@click="addMember"
|
|
>
|
|
{{ t('generic.add') }}
|
|
</button>
|
|
</template>
|
|
<template #remove-button="{remove, i}">
|
|
<span v-if="(isCreate && i === 0) || isView" />
|
|
<button
|
|
v-else
|
|
type="button"
|
|
:disabled="isView"
|
|
class="btn role-link"
|
|
@click="remove"
|
|
>
|
|
{{ t('generic.remove') }}
|
|
</button>
|
|
</template>
|
|
</ArrayList>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.role {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-direction: row;
|
|
}
|
|
</style>
|