mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-08 14:15:35 +08:00
Finish experiment table [SCI-9800]
This commit is contained in:
parent
a894d6bd26
commit
985dddef17
16 changed files with 6270 additions and 7159 deletions
|
@ -7,7 +7,15 @@ module AccessPermissions
|
|||
before_action :check_read_permissions, only: %i(show)
|
||||
before_action :check_manage_permissions, only: %i(edit update)
|
||||
|
||||
def show; end
|
||||
def show
|
||||
render json: @experiment.user_assignments.includes(:user_role, :user).order('users.full_name ASC'),
|
||||
each_serializer: UserAssignmentSerializer
|
||||
end
|
||||
|
||||
def new
|
||||
render json: @available_users, each_serializer: UserSerializer
|
||||
end
|
||||
|
||||
|
||||
def edit; end
|
||||
|
||||
|
@ -36,7 +44,7 @@ module AccessPermissions
|
|||
|
||||
log_change_activity
|
||||
|
||||
render :experiment_member
|
||||
render json: {}, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
|
74
app/javascript/vue/experiments/card.vue
Normal file
74
app/javascript/vue/experiments/card.vue
Normal file
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div class="p-4 rounded sn-shadow-flyout flex flex-col">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<div class="sci-checkbox-container">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="sci-checkbox"
|
||||
@change="itemSelected"
|
||||
/>
|
||||
<label :for="params.id" class="sci-checkbox-label"></label>
|
||||
</div>
|
||||
<div>{{ params.code }}</div>
|
||||
<RowMenuRenderer :params="{data: params, dtComponent: dtComponent}" class="ml-auto"/>
|
||||
</div>
|
||||
<a :href="params.urls.show"
|
||||
:class="{'pointer-events-none text-sn-grey': !params.urls.show}"
|
||||
class="font-bold mb-4 text-sn-black hover:no-underline hover:text-sn-black">
|
||||
{{ params.name }}
|
||||
</a>
|
||||
<div class="flex">
|
||||
<div class="grid gap-2 grid-cols-[100px_auto] mt-auto">
|
||||
<span class="text-sn-grey">{{ i18n.t('experiments.card.start_date') }}</span>
|
||||
<span class="font-bold">{{ params.created_at }}</span>
|
||||
|
||||
<span class="text-sn-grey">{{ i18n.t('experiments.card.modified_date') }}</span>
|
||||
<span class="font-bold">{{ params.updated_at }}</span>
|
||||
|
||||
<template v-if="params.archived_on">
|
||||
<span class="text-sn-grey">{{ i18n.t('experiments.card.archived_date') }}</span>
|
||||
<span class="font-bold">{{ params.archived_on }}</span>
|
||||
</template>
|
||||
|
||||
<span class="text-sn-grey">{{ i18n.t('experiments.card.completed_task') }}</span>
|
||||
<span class="font-bold">{{ i18n.t(
|
||||
'experiments.card.completed_value', {
|
||||
completed: params.completed_tasks,
|
||||
all: params.total_tasks
|
||||
}
|
||||
) }}</span>
|
||||
</div>
|
||||
<div class="h-20 w-20 p-0.5 bg-sn-light-grey rounded-sm shrink-0 ml-auto">
|
||||
<img :src="params.workflow_img" class="max-h-18 max-w-[72px]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<div class="w-full h-1 bg-sn-light-grey">
|
||||
<div class="h-full bg-sn-blue" :style="{
|
||||
width: params.completed_tasks / params.total_tasks * 100 + '%'
|
||||
}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<Description :params="{data: params, value: params.description, dtComponent: dtComponent}" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import RowMenuRenderer from '../shared/datatable/row_menu_renderer.vue';
|
||||
import CardSelectorMixin from '../shared/datatable/mixins/card_selector.js';
|
||||
import Description from './renderers/description.vue';
|
||||
|
||||
export default {
|
||||
name: 'ProjectCard',
|
||||
props: {
|
||||
params: Object,
|
||||
dtComponent: Object,
|
||||
},
|
||||
components: {
|
||||
RowMenuRenderer,
|
||||
Description,
|
||||
},
|
||||
mixins: [CardSelectorMixin],
|
||||
};
|
||||
</script>
|
|
@ -19,8 +19,12 @@
|
|||
@duplicate="duplicate"
|
||||
@move="move"
|
||||
@edit="edit"
|
||||
@create="create"
|
||||
@access="access"
|
||||
>
|
||||
<template> </template>
|
||||
<template #card="data">
|
||||
<ExperimentCard :params="data.params" :dtComponent="data.dtComponent" ></ExperimentCard>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<ConfirmationModal
|
||||
|
@ -48,6 +52,13 @@
|
|||
:experiment="editModalObject"
|
||||
@close="editModalObject = null"
|
||||
@update="updateTable"/>
|
||||
<NewModal
|
||||
v-if="newModalOpen"
|
||||
:createUrl="createUrl"
|
||||
@close="newModalOpen = false"
|
||||
@create="updateTable"/>
|
||||
<AccessModal v-if="accessModalParams" :params="accessModalParams"
|
||||
@close="accessModalParams = null" @refresh="this.reloadingTable = true" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -63,6 +74,9 @@ import DescriptionModal from './modals/description.vue';
|
|||
import DuplicateModal from './modals/duplicate.vue';
|
||||
import MoveModal from './modals/move.vue';
|
||||
import EditModal from './modals/edit.vue';
|
||||
import NewModal from './modals/new.vue';
|
||||
import AccessModal from '../shared/access_modal/modal.vue';
|
||||
import ExperimentCard from './card.vue';
|
||||
|
||||
export default {
|
||||
name: 'ExperimentsList',
|
||||
|
@ -73,6 +87,9 @@ export default {
|
|||
DuplicateModal,
|
||||
MoveModal,
|
||||
EditModal,
|
||||
NewModal,
|
||||
AccessModal,
|
||||
ExperimentCard,
|
||||
},
|
||||
props: {
|
||||
dataSource: { type: String, required: true },
|
||||
|
@ -80,9 +97,13 @@ export default {
|
|||
activePageUrl: { type: String },
|
||||
archivedPageUrl: { type: String },
|
||||
currentViewMode: { type: String, required: true },
|
||||
createUrl: { type: String, required: true },
|
||||
userRolesUrl: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
accessModalParams: null,
|
||||
newModalOpen: false,
|
||||
editModalObject: null,
|
||||
moveModalObject: null,
|
||||
duplicateModalObject: null,
|
||||
|
@ -174,13 +195,27 @@ export default {
|
|||
type: 'DateRange',
|
||||
label: this.i18n.t('filters_modal.created_on.label'),
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
type: 'DateRange',
|
||||
label: this.i18n.t('filters_modal.updated_on.label'),
|
||||
},
|
||||
];
|
||||
|
||||
if (this.currentViewMode === 'archived') {
|
||||
filters.push({
|
||||
key: 'archived_on',
|
||||
type: 'DateRange',
|
||||
label: this.i18n.t('filters_modal.archived_on.label'),
|
||||
});
|
||||
}
|
||||
|
||||
return filters;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateTable() {
|
||||
this.newModalOpen = false;
|
||||
this.editModalObject = null;
|
||||
this.moveModalObject = null;
|
||||
this.duplicateModalObject = null;
|
||||
|
@ -218,6 +253,15 @@ export default {
|
|||
edit(_e, experiment) {
|
||||
[this.editModalObject] = experiment;
|
||||
},
|
||||
create() {
|
||||
this.newModalOpen = true;
|
||||
},
|
||||
access(event, rows) {
|
||||
this.accessModalParams = {
|
||||
object: rows[0],
|
||||
roles_path: this.userRolesUrl,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
82
app/javascript/vue/experiments/modals/new.vue
Normal file
82
app/javascript/vue/experiments/modals/new.vue
Normal file
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div ref="modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title truncate !block">
|
||||
{{ i18n.t('experiments.new.modal_title') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="sci-label">{{ i18n.t('experiments.new.name') }}</label>
|
||||
<div class="sci-input-container-v2 mb-4">
|
||||
<input type="text" class="sci-input-field"
|
||||
v-model="name"
|
||||
autofocus
|
||||
:placeholder="i18n.t('experiments.new.name_placeholder')">
|
||||
</div>
|
||||
<label class="sci-label">{{ i18n.t('experiments.new.description') }}</label>
|
||||
<div class="sci-input-container-v2 h-40">
|
||||
<textarea class="sci-input-field"
|
||||
ref="description"
|
||||
v-model="description">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button type="button" :disabled="!validName" class="btn btn-primary" @click="submit">
|
||||
{{ i18n.t('experiments.new.modal_create') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global HelperModule SmartAnnotation */
|
||||
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
import modalMixin from '../../shared/modal_mixin';
|
||||
|
||||
export default {
|
||||
name: 'NewModal',
|
||||
props: {
|
||||
createUrl: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
description: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
validName() {
|
||||
return this.name.length > 0;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
SmartAnnotation.init($(this.$refs.description), false);
|
||||
},
|
||||
mixins: [modalMixin],
|
||||
methods: {
|
||||
submit() {
|
||||
axios.post(this.createUrl, {
|
||||
experiment: {
|
||||
name: this.name,
|
||||
description: this.$refs.description.value,
|
||||
},
|
||||
}).then((response) => {
|
||||
this.$emit('create');
|
||||
window.location.replace(response.data.path);
|
||||
}).catch((error) => {
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,20 +1,30 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
|
||||
<button type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
|
||||
<h4 class="modal-title truncate !block">
|
||||
{{ i18n.t(`access_permissions.${params.object.type}.modals.edit_modal.title`, {resource_name: params.object.name}) }}
|
||||
{{ i18n.t(`access_permissions.${params.object.type}.modals.edit_modal.title`, {
|
||||
resource_name: params.object.name
|
||||
}) }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="h-[60vh] overflow-y-auto">
|
||||
<div v-for="userAssignment in manuallyAssignedUsers" :key="userAssignment.id" class="p-2 flex items-center gap-2">
|
||||
<div v-for="userAssignment in manuallyAssignedUsers"
|
||||
:key="userAssignment.id"
|
||||
class="p-2 flex items-center gap-2">
|
||||
<div>
|
||||
<img :src="userAssignment.attributes.user.avatar_url" class="rounded-full w-8 h-8">
|
||||
</div>
|
||||
<div>{{ userAssignment.attributes.user.name }}</div>
|
||||
<div>
|
||||
<div>{{ userAssignment.attributes.user.name }}</div>
|
||||
<div class="text-xs text-sn-grey">{{ userAssignment.attributes.inherit_message }}</div>
|
||||
</div>
|
||||
<MenuDropdown
|
||||
v-if="!userAssignment.attributes.last_owner"
|
||||
v-if="!userAssignment.attributes.last_owner && params.object.urls.update_access"
|
||||
class="ml-auto"
|
||||
:listItems="rolesFromatted"
|
||||
:btnText="userAssignment.attributes.user_role.name"
|
||||
|
@ -35,8 +45,10 @@
|
|||
<div>
|
||||
{{ i18n.t('access_permissions.everyone_else', { team_name: params.object.team }) }}
|
||||
</div>
|
||||
<i class="sn-icon sn-icon-info" :title='this.autoAssignedUsers.map((ua) => ua.attributes.user.name).join("\u000d")'></i>
|
||||
<i class="sn-icon sn-icon-info"
|
||||
:title='this.autoAssignedUsers.map((ua) => ua.attributes.user.name).join("\u000d")'></i>
|
||||
<MenuDropdown
|
||||
v-if="params.object.top_level_assignable && params.object.urls.update_access"
|
||||
class="ml-auto"
|
||||
:listItems="rolesFromatted"
|
||||
:btnText="this.roles.find((role) => role[0] == default_role)[1]"
|
||||
|
@ -45,10 +57,14 @@
|
|||
@setRole="(...args) => this.changeDefaultRole(...args)"
|
||||
@removeRole="() => this.changeDefaultRole()"
|
||||
></MenuDropdown>
|
||||
<div class="ml-auto btn btn-light pointer-events-none" v-else>
|
||||
{{ this.roles.find((role) => role[0] == default_role)[1] }}
|
||||
<div class="h-6 w-6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="params.object.top_level_assignable" class="modal-footer">
|
||||
<div v-if="params.object.urls.new_access" class="modal-footer">
|
||||
<button class="btn-light ml-auto btn" @click="$emit('changeMode', 'newView')">
|
||||
<i class="sn-icon sn-icon-new-task"></i>
|
||||
{{ i18n.t('access_permissions.grant_access') }}
|
||||
|
@ -58,21 +74,23 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MenuDropdown from "../../shared/menu_dropdown.vue";
|
||||
/* global HelperModule */
|
||||
|
||||
import MenuDropdown from '../menu_dropdown.vue';
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
params: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
visible: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
default_role: {
|
||||
type: Number
|
||||
}
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
emits: ['changeMode', 'modified'],
|
||||
mounted() {
|
||||
|
@ -84,31 +102,43 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
rolesFromatted() {
|
||||
let roles = this.roles.map((role) => {
|
||||
return {
|
||||
let roles = [];
|
||||
|
||||
if (!this.params.object.top_level_assignable) {
|
||||
roles.push({
|
||||
emit: 'setRole',
|
||||
text: this.i18n.t('access_permissions.reset'),
|
||||
params: 'reset',
|
||||
});
|
||||
}
|
||||
|
||||
roles = roles.concat(this.roles.map((role) => (
|
||||
{
|
||||
emit: 'setRole',
|
||||
text: role[1],
|
||||
params: role[0]
|
||||
params: role[0],
|
||||
}
|
||||
});
|
||||
)));
|
||||
|
||||
roles.push({
|
||||
dividerBefore: true,
|
||||
emit: 'removeRole',
|
||||
text: this.i18n.t('access_permissions.remove_access'),
|
||||
});
|
||||
if (this.params.object.top_level_assignable) {
|
||||
roles.push({
|
||||
dividerBefore: true,
|
||||
emit: 'removeRole',
|
||||
text: this.i18n.t('access_permissions.remove_access'),
|
||||
});
|
||||
}
|
||||
|
||||
return roles
|
||||
return roles;
|
||||
},
|
||||
manuallyAssignedUsers() {
|
||||
return this.assignedUsers.filter((user) => {
|
||||
return user.attributes.assigned === 'manually';
|
||||
});
|
||||
return this.assignedUsers.filter((user) => (
|
||||
user.attributes.assigned === 'manually'
|
||||
));
|
||||
},
|
||||
autoAssignedUsers() {
|
||||
return this.assignedUsers.filter((user) => {
|
||||
return user.attributes.assigned === 'automatically';
|
||||
});
|
||||
return this.assignedUsers.filter((user) => (
|
||||
user.attributes.assigned === 'automatically'
|
||||
));
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
@ -123,56 +153,55 @@ export default {
|
|||
axios.get(this.params.object.urls.show_access)
|
||||
.then((response) => {
|
||||
this.assignedUsers = response.data.data;
|
||||
})
|
||||
});
|
||||
},
|
||||
getRoles() {
|
||||
axios.get(this.params.roles_path)
|
||||
.then((response) => {
|
||||
this.roles = response.data.data;
|
||||
})
|
||||
});
|
||||
},
|
||||
changeRole(id, role_id) {
|
||||
axios.put(this.params.object.urls.show_access, {
|
||||
changeRole(id, roleId) {
|
||||
axios.put(this.params.object.urls.update_access, {
|
||||
user_assignment: {
|
||||
user_id: id,
|
||||
user_role_id: role_id
|
||||
}
|
||||
}).then((response) => {
|
||||
user_role_id: roleId,
|
||||
},
|
||||
}).then(() => {
|
||||
this.$emit('modified');
|
||||
this.getAssignedUsers();
|
||||
})
|
||||
});
|
||||
},
|
||||
removeRole(id) {
|
||||
axios.delete(this.params.object.urls.show_access, {
|
||||
axios.delete(this.params.object.urls.update_access, {
|
||||
data: {
|
||||
user_id: id
|
||||
}
|
||||
user_id: id,
|
||||
},
|
||||
}).then((response) => {
|
||||
this.$emit('modified');
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
this.getAssignedUsers();
|
||||
})
|
||||
});
|
||||
},
|
||||
changeDefaultRole(role_id) {
|
||||
changeDefaultRole(roleId) {
|
||||
axios.put(this.params.object.urls.default_public_user_role_path, {
|
||||
project: {
|
||||
default_public_user_role_id: role_id || ''
|
||||
}
|
||||
default_public_user_role_id: roleId || '',
|
||||
},
|
||||
}).then((response) => {
|
||||
this.$emit('modified');
|
||||
if (!role_id) {
|
||||
if (!roleId) {
|
||||
this.$emit('changeVisibility', false, null);
|
||||
} else {
|
||||
this.$emit('changeVisibility', true, role_id);
|
||||
this.$emit('changeVisibility', true, roleId);
|
||||
}
|
||||
if (response.data.message) {
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
removeDefaultRole() {
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -18,25 +18,24 @@
|
|||
|
||||
<script>
|
||||
|
||||
import SelectDropdown from "../../shared/select_dropdown.vue";
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
import modal_mixin from "../../shared/modal_mixin";
|
||||
import SelectDropdown from '../select_dropdown.vue';
|
||||
import modalMixin from '../modal_mixin';
|
||||
import editView from './edit.vue';
|
||||
import newView from './new.vue';
|
||||
|
||||
export default {
|
||||
name: "AccessModal",
|
||||
name: 'AccessModal',
|
||||
props: {
|
||||
params: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
mixins: [modal_mixin],
|
||||
mixins: [modalMixin],
|
||||
components: {
|
||||
SelectDropdown,
|
||||
editView,
|
||||
newView
|
||||
newView,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -63,6 +62,6 @@ export default {
|
|||
this.visible = status;
|
||||
this.default_role = role;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<i class="sn-icon sn-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title truncate flex items-center gap-4">
|
||||
{{ i18n.t('access_permissions.partials.new_assignments_form.title', {resource_name: params.object.name}) }}
|
||||
<button class="close" @click="$emit('changeMode', 'editView')">
|
||||
|
@ -12,7 +14,9 @@
|
|||
<div class="modal-body">
|
||||
<div class="mb-4">
|
||||
<div class="sci-input-container-v2 left-icon">
|
||||
<input type="text" v-model="query" class="sci-input-field" autofocus="true" :placeholder="i18n.t('access_permissions.partials.new_assignments_form.find_people_html')" />
|
||||
<input type="text" v-model="query" class="sci-input-field"
|
||||
autofocus="true"
|
||||
:placeholder="i18n.t('access_permissions.partials.new_assignments_form.find_people_html')" />
|
||||
<i class="sn-icon sn-icon-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -53,20 +57,21 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MenuDropdown from "../../shared/menu_dropdown.vue";
|
||||
/* global HelperModule */
|
||||
import MenuDropdown from '../menu_dropdown.vue';
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
params: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
visible: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
default_role: {
|
||||
type: Number
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
emits: ['changeMode'],
|
||||
|
@ -79,19 +84,19 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
rolesFromatted() {
|
||||
return this.roles.map((role) => {
|
||||
return {
|
||||
return this.roles.map((role) => (
|
||||
{
|
||||
emit: 'setRole',
|
||||
text: role[1],
|
||||
params: role[0]
|
||||
params: role[0],
|
||||
}
|
||||
});
|
||||
));
|
||||
},
|
||||
filteredUsers() {
|
||||
return this.unAssignedUsers.filter((user) => {
|
||||
return user.attributes.name.toLowerCase().includes(this.query.toLowerCase());
|
||||
});
|
||||
}
|
||||
return this.unAssignedUsers.filter((user) => (
|
||||
user.attributes.name.toLowerCase().includes(this.query.toLowerCase())
|
||||
));
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -105,20 +110,20 @@ export default {
|
|||
axios.get(this.params.object.urls.new_access)
|
||||
.then((response) => {
|
||||
this.unAssignedUsers = response.data.data;
|
||||
})
|
||||
});
|
||||
},
|
||||
getRoles() {
|
||||
axios.get(this.params.roles_path)
|
||||
.then((response) => {
|
||||
this.roles = response.data.data;
|
||||
})
|
||||
});
|
||||
},
|
||||
assignRole(id, role_id) {
|
||||
assignRole(id, roleId) {
|
||||
axios.post(this.params.object.urls.create_access, {
|
||||
user_assignment: {
|
||||
user_id: id,
|
||||
user_role_id: role_id
|
||||
}
|
||||
user_role_id: roleId,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
this.$emit('modified');
|
||||
|
@ -126,11 +131,10 @@ export default {
|
|||
this.getUnAssignedUsers();
|
||||
|
||||
if (id === 'all') {
|
||||
this.$emit('changeVisibility', true, role_id);
|
||||
this.$emit('changeVisibility', true, roleId);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -8,7 +8,8 @@ module Lists
|
|||
include ActionView::Helpers::TextHelper
|
||||
|
||||
attributes :name, :code, :created_at, :updated_at, :workflow_img, :description, :completed_tasks,
|
||||
:total_tasks, :archived_on, :urls, :sa_description
|
||||
:total_tasks, :archived_on, :urls, :sa_description, :default_public_user_role_id, :team,
|
||||
:top_level_assignable, :hidden
|
||||
|
||||
def created_at
|
||||
I18n.l(object.created_at, format: :full_date)
|
||||
|
@ -22,6 +23,22 @@ module Lists
|
|||
team: object.project.team)
|
||||
end
|
||||
|
||||
def default_public_user_role_id
|
||||
object.project.default_public_user_role_id
|
||||
end
|
||||
|
||||
def hidden
|
||||
object.project.hidden?
|
||||
end
|
||||
|
||||
def top_level_assignable
|
||||
false
|
||||
end
|
||||
|
||||
def team
|
||||
object.project.team.name
|
||||
end
|
||||
|
||||
def updated_at
|
||||
I18n.l(object.updated_at, format: :full_date)
|
||||
end
|
||||
|
@ -43,15 +60,21 @@ module Lists
|
|||
end
|
||||
|
||||
def urls
|
||||
{
|
||||
urls_list = {
|
||||
show: table_experiment_path(object),
|
||||
actions: actions_toolbar_experiments_path(items: [{ id: object.id }].to_json),
|
||||
projects_to_clone: projects_to_clone_experiment_path(object),
|
||||
projects_to_move: projects_to_move_experiment_path(object),
|
||||
clone: clone_experiment_path(object),
|
||||
move: move_experiment_path(object),
|
||||
update: experiment_path(object)
|
||||
update: experiment_path(object),
|
||||
show_access: access_permissions_experiment_path(object)
|
||||
}
|
||||
|
||||
if can_manage_project_users?(object.project)
|
||||
urls_list[:update_access] = access_permissions_experiment_path(object)
|
||||
end
|
||||
urls_list
|
||||
end
|
||||
|
||||
def workflow_img
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Lists
|
||||
class ProjectAndFolderSerializer < ActiveModel::Serializer
|
||||
include Rails.application.routes.url_helpers
|
||||
|
@ -64,8 +66,9 @@ module Lists
|
|||
project_folder_path(object)
|
||||
end
|
||||
|
||||
urls_list[:show_access] = access_permissions_project_path(object)
|
||||
if project? && can_manage_project_users?(object)
|
||||
urls_list[:show_access] = access_permissions_project_path(object)
|
||||
urls_list[:update_access] = access_permissions_project_path(object)
|
||||
urls_list[:new_access] = new_access_permissions_project_path(id: object.id)
|
||||
urls_list[:create_access] = access_permissions_projects_path(id: object.id)
|
||||
urls_list[:default_public_user_role_path] =
|
||||
|
|
|
@ -4,7 +4,15 @@ class UserAssignmentSerializer < ActiveModel::Serializer
|
|||
include Canaid::Helpers::PermissionsHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
attributes :id, :assigned, :assignable_type, :user, :user_role, :last_owner
|
||||
attributes :id, :assigned, :assignable_type, :user, :user_role, :last_owner, :inherit_message
|
||||
|
||||
def assigned
|
||||
parent_assignment(parent).assigned
|
||||
end
|
||||
|
||||
def parent
|
||||
object.assignable.permission_parent
|
||||
end
|
||||
|
||||
def user
|
||||
{
|
||||
|
@ -22,6 +30,36 @@ class UserAssignmentSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def last_owner
|
||||
object.last_with_permission?("#{object.assignable.class.name}Permissions".constantize::USERS_MANAGE, assigned: :manually)
|
||||
parent_assignment(parent).last_with_permission?(ProjectPermissions::USERS_MANAGE, assigned: :manually)
|
||||
end
|
||||
|
||||
def inherit_message
|
||||
user_assignment_resource_role_name(object.user, object.assignable, inherit = '')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parent_assignment(parent)
|
||||
return object if parent.blank?
|
||||
|
||||
case parent
|
||||
when Project
|
||||
parent.user_assignments.find_by(user: object.user)
|
||||
when Experiment
|
||||
parent_assigned(parent.permission_parent)
|
||||
end
|
||||
end
|
||||
|
||||
def user_assignment_resource_role_name(user, resource, inherit = '')
|
||||
user_assignment = resource.user_assignments.find_by(user: user)
|
||||
|
||||
return '' if [Project, Protocol].include?(resource.class) && inherit.blank?
|
||||
|
||||
if user_assignment.automatically_assigned? && resource.permission_parent.present?
|
||||
parent = resource.permission_parent
|
||||
return user_assignment_resource_role_name(user, parent, '_inherit')
|
||||
end
|
||||
|
||||
I18n.t("access_permissions.partials.#{resource.class.to_s.downcase}_tooltip#{inherit}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ module Lists
|
|||
@raw_data = raw_data
|
||||
@params = params
|
||||
@user = user
|
||||
@filters = params[:filters] || {}
|
||||
end
|
||||
|
||||
def call
|
||||
|
|
|
@ -33,12 +33,37 @@ module Lists
|
|||
end
|
||||
|
||||
def filter_records(records)
|
||||
return records unless @params[:search]
|
||||
if @filters[:query].present?
|
||||
records = records.where_attributes_like(
|
||||
['experiments.name', 'experiments.description', "('EX' || experiments.id)"],
|
||||
@filters[:query]
|
||||
)
|
||||
end
|
||||
|
||||
records.where_attributes_like(
|
||||
['experiments.name', 'experiments.description', "('EX' || experiments.id)"],
|
||||
@params[:search]
|
||||
)
|
||||
if @filters[:created_at_from].present?
|
||||
records = records.where('experiments.created_at > ?', @filters[:created_at_from])
|
||||
end
|
||||
if @filters[:created_at_to].present?
|
||||
records = records.where('experiments.created_at < ?',
|
||||
@filters[:created_at_to])
|
||||
end
|
||||
if @filters[:updated_on_from].present?
|
||||
records = records.where('experiments.updated_at > ?', @filters[:updated_on_from])
|
||||
end
|
||||
if @filters[:updated_on_to].present?
|
||||
records = records.where('experiments.updated_at < ?',
|
||||
@filters[:updated_on_to])
|
||||
end
|
||||
|
||||
if @filters[:archived_on_from].present?
|
||||
records = records.where('COALESCE(experiments.archived_on, projects.archived_on) > ?',
|
||||
@filters[:archived_on_from])
|
||||
end
|
||||
if @filters[:archived_on_to].present?
|
||||
records = records.where('COALESCE(experiments.archived_on, projects.archived_on) < ?',
|
||||
@filters[:archived_on_to])
|
||||
end
|
||||
records
|
||||
end
|
||||
|
||||
def sortable_columns
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
<div id="ExperimentsList" class="fixed-content-body">
|
||||
<experiments-list
|
||||
actions-url="<%= actions_toolbar_experiments_path %>"
|
||||
create-url="<%= project_experiments_path(@project) %>"
|
||||
data-source="<%= experiments_path(project_id: @project, format: :json) %>"
|
||||
active-page-url="<%= experiments_path(project_id: @project, view_mode: :active) %>"
|
||||
archived-page-url="<%= experiments_path(project_id: @project, view_mode: :archived) %>"
|
||||
current-view-mode="<%= params[:view_mode] || :active %>"
|
||||
user-roles-url="<%= user_roles_projects_path %>"
|
||||
/>
|
||||
</div>
|
||||
<%= javascript_include_tag 'vue_experiments_list' %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3646,8 +3646,8 @@ en:
|
|||
reset: "Inherit role"
|
||||
reset_description: "The inherited role from project or experiment will be applied"
|
||||
projects: "Project"
|
||||
projects_tooltip: "This role was set on this project."
|
||||
projects_tooltip_inherit: "This role was inherited from the project."
|
||||
project_tooltip: "This role was set on this project."
|
||||
project_tooltip_inherit: "This role was inherited from the project."
|
||||
protocol: "Protocol"
|
||||
protocol_tooltip: "This role was set on this protocol."
|
||||
protocol_tooltip_inherit: "This role was inherited from the protocol."
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
"croppie": "^2.6.4",
|
||||
"css-loader": "^6.7.3",
|
||||
"decimal.js": "^10.3.1",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"glob": "^7.1.2",
|
||||
|
|
Loading…
Add table
Reference in a new issue