Create a modal for item assignment from inventory page [SCI-8250]

- Fix the behavior of the search_select.
- Fix select options positioning.
- Get the items to assign from selected rows.
- Style the modal.

Style the modal [SCI-8250]

Fix select options positioning [SCI-8250]
This commit is contained in:
sboursen-scinote 2023-05-05 10:31:35 +02:00
parent 443cd21090
commit 5f8eafedf9
7 changed files with 225 additions and 88 deletions

View file

@ -278,6 +278,10 @@ var RepositoryDatatable = (function(global) {
}); });
} }
function updateSelectedRowsForAssignments() {
window.AssignItemsToTaskModalComponent.setShowCallback(() => rowsSelected);
}
function checkAvailableColumns() { function checkAvailableColumns() {
$.ajax({ $.ajax({
url: $(TABLE_ID).data('available-columns'), url: $(TABLE_ID).data('available-columns'),
@ -732,6 +736,7 @@ var RepositoryDatatable = (function(global) {
}) })
initRowSelection(); initRowSelection();
updateSelectedRowsForAssignments();
// $(window).resize(() => { // $(window).resize(() => {
// setTimeout(() => { // setTimeout(() => {
// adjustTableHeader(); // adjustTableHeader();

View file

@ -0,0 +1,37 @@
.assign-items-to-task-modal-container {
.modal-header {
padding: 1rem;
display: flex;
color: $color-volcano;
font-size: $font-size-h2;
font-weight: bold;
.close {
margin-left: auto;
}
}
.modal-body {
display: flex;
flex-direction: column;
gap: 1rem;
color: $color-volcano;
font-size: $font-size-base;
.level-selector {
flex-direction: column;
display: flex;
gap: .25rem;
label {
margin-bottom: 0;
font-weight: bold;
font-size: $font-size-h6;
}
}
}
.modal-footer {
padding: 1rem;
}
}

View file

@ -8,7 +8,7 @@ Vue.prototype.i18n = window.I18n;
function initAssignItemsToTaskModalComponent() { function initAssignItemsToTaskModalComponent() {
const container = $('.assign-items-to-task-modal-container'); const container = $('.assign-items-to-task-modal-container');
if (container.length) { if (container.length) {
window.AssignItemsToTaskModalComponent = new Vue({ window.AssignItemsToTaskModalComponentContainer = new Vue({
el: '.assign-items-to-task-modal-container', el: '.assign-items-to-task-modal-container',
name: 'AssignItemsToTaskModalComponent', name: 'AssignItemsToTaskModalComponent',
components: { components: {

View file

@ -7,7 +7,7 @@
role="dialog" role="dialog"
aria-labelledby="assignItemsToTaskModalLabel" aria-labelledby="assignItemsToTaskModalLabel"
> >
<div class="modal-dialog" role="document"> <div class="modal-dialog modal-sm" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title"> <h4 class="modal-title">
@ -29,23 +29,39 @@
}} }}
</div> </div>
<div class="project-selector"> <div class="project-selector level-selector">
<label> <label>
{{ i18n.t("repositories.modal_assign_items_to_task.body.project_select.label") }} {{
i18n.t(
"repositories.modal_assign_items_to_task.body.project_select.label"
)
}}
</label> </label>
<SelectSearch <SelectSearch
ref="projectsSelector" ref="projectsSelector"
@change="changeProject" @change="changeProject"
:options="projects" :options="projects"
:placeholder="i18n.t('repositories.modal_assign_items_to_task.body.project_select.placeholder')" :placeholder="
:searchPlaceholder="i18n.t('repositories.modal_assign_items_to_task.body.project_select.placeholder')" i18n.t(
'repositories.modal_assign_items_to_task.body.project_select.placeholder'
)
"
:searchPlaceholder="
i18n.t(
'repositories.modal_assign_items_to_task.body.project_select.placeholder'
)
"
/> />
</div> </div>
<div class="experiment-selector"> <div class="experiment-selector level-selector">
<label> <label>
{{ i18n.t("repositories.modal_assign_items_to_task.body.experiment_select.label") }} {{
i18n.t(
"repositories.modal_assign_items_to_task.body.experiment_select.label"
)
}}
</label> </label>
<SelectSearch <SelectSearch
@ -54,13 +70,21 @@
@change="changeExperiment" @change="changeExperiment"
:options="experiments" :options="experiments"
:placeholder="experimentsSelectorPlaceholder" :placeholder="experimentsSelectorPlaceholder"
:searchPlaceholder="i18n.t('repositories.modal_assign_items_to_task.body.experiment_select.placeholder')" :searchPlaceholder="
i18n.t(
'repositories.modal_assign_items_to_task.body.experiment_select.placeholder'
)
"
/> />
</div> </div>
<div class="task-selector"> <div class="task-selector level-selector">
<label> <label>
{{ i18n.t("repositories.modal_assign_items_to_task.body.task_select.label") }} {{
i18n.t(
"repositories.modal_assign_items_to_task.body.task_select.label"
)
}}
</label> </label>
<SelectSearch <SelectSearch
@ -68,16 +92,28 @@
ref="tasksSelector" ref="tasksSelector"
@change="changeTask" @change="changeTask"
:options="tasks" :options="tasks"
:placeholder="i18n.t('repositories.modal_assign_items_to_task.body.task_select.disabled_placeholder')" :placeholder="
:searchPlaceholder="i18n.t('repositories.modal_assign_items_to_task.body.task_select.placeholder')" i18n.t(
'repositories.modal_assign_items_to_task.body.task_select.disabled_placeholder'
)
"
:searchPlaceholder="
i18n.t(
'repositories.modal_assign_items_to_task.body.task_select.placeholder'
)
"
/> />
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"> <button
{{ type="button"
i18n.t("repositories.modal_assign_items_to_task.assign") class="btn btn-primary"
}} data-dismiss="modal"
:disabled="!selectedTask"
@click="assign"
>
{{ i18n.t("repositories.modal_assign_items_to_task.assign") }}
</button> </button>
</div> </div>
</div> </div>
@ -86,77 +122,90 @@
</template> </template>
<script> <script>
import SelectSearch from '../shared/select_search.vue' import SelectSearch from "../shared/select_search.vue";
export default { export default {
name: "AssignItemsToTaskModalContainer", name: "AssignItemsToTaskModalContainer",
props: { props: {
visibility: Boolean, visibility: Boolean,
urls: Object, urls: Object
}, },
data() { data() {
return { return {
projects: [ rowsToAssign: [],
[1, "project1"], projects: [],
[2, "project2"], experiments: [],
[3, "project3"], tasks: [],
[4, "project4"],
[5, "project5"],
[6, "project6"],
[7, "project7"],
[8, "project8"],
],
experiments: [
[1, "experiment1"],
[2, "experiment2"],
[3, "experiment3"],
[4, "experiment4"],
[5, "experiment5"],
[6, "experiment6"],
[7, "experiment7"],
[8, "experiment8"],
],
tasks: [
[1, "task1"],
[2, "task2"],
[3, "task3"],
[4, "task4"],
[5, "task5"],
[6, "task6"],
[7, "task7"],
[8, "task8"],
],
selectedProject: null, selectedProject: null,
selectedExperiment: null, selectedExperiment: null,
selectedTask: null selectedTask: null,
showCallback: null
}; };
}, },
components: { components: {
SelectSearch SelectSearch
}, },
created() {
window.AssignItemsToTaskModalComponent = this;
},
mounted() { mounted() {
$.get(this.urls.projects) $(this.$refs.modal).on("shown.bs.modal", () => {
$(this.$refs.modal).on('hidden.bs.modal', () => { $.get(this.projectURL, data => {
this.$emit('close'); if (Array.isArray(data)) {
this.projects = data;
return false;
}
this.projects = [];
}); });
});
$(this.$refs.modal).on("hidden.bs.modal", () => {
this.$emit("close");
});
},
beforeDestroy() {
delete window.AssignItemsToTaskModalComponent;
}, },
computed: { computed: {
experimentsSelectorPlaceholder() { experimentsSelectorPlaceholder() {
if (this.selectedProject) { if (this.selectedProject) {
return this.i18n.t('repositories.modal_assign_items_to_task.body.experiment_select.placeholder'); return this.i18n.t(
"repositories.modal_assign_items_to_task.body.experiment_select.placeholder"
);
} }
return this.i18n.t('repositories.modal_assign_items_to_task.body.experiment_select.disabled_placeholder') return this.i18n.t(
"repositories.modal_assign_items_to_task.body.experiment_select.disabled_placeholder"
);
}, },
tasksSelectorPlaceholder() { tasksSelectorPlaceholder() {
if (this.selectedExperiment) { if (this.selectedExperiment) {
return this.i18n.t('repositories.modal_assign_items_to_task.body.task_select.placeholder'); return this.i18n.t(
"repositories.modal_assign_items_to_task.body.task_select.placeholder"
);
} }
return this.i18n.t('repositories.modal_assign_items_to_task.body.task_select.disabled_placeholder') return this.i18n.t(
"repositories.modal_assign_items_to_task.body.task_select.disabled_placeholder"
);
},
projectURL() {
return `${this.urls.projects}`;
},
experimentURL() {
return `${this.urls.experiments}?project_id=${this.selectedProject ||
""}`;
},
taskURL() {
return `${this.urls.tasks}?experiment_id=${this.selectedExperiment ||
""}`;
},
assignURL() {
return this.urls.assign.replace(":module_id", this.selectedTask);
} }
}, },
watch: { watch: {
visibility(newVal) { visibility() {
if (newVal) { if (this.visibility) {
this.showModal(); this.showModal();
} else { } else {
this.hideModal(); this.hideModal();
@ -165,33 +214,76 @@ export default {
}, },
methods: { methods: {
showModal() { showModal() {
$(this.$refs.modal).modal('show'); $(this.$refs.modal).modal("show");
this.rowsToAssign = this.showCallback();
}, },
hideModal(){ hideModal() {
$(this.$refs.modal).modal('hide'); $(this.$refs.modal).modal("hide");
}, },
changeProject(value) { changeProject(value) {
const newProjectVal = value; this.selectedProject = value;
this.selectedProject = null; this.resetExperimentSelector();
this.selectedExperiment = null; this.resetTaskSelector();
this.selectedTask = null;
setTimeout(() => {
this.experiments = [
[1, 'ex1'],
[2, 'ex2'],
[3, 'ex3'],
[4, 'ex4']
];
this.selectedProject = newProjectVal;
}, 2000);
$.get(this.experimentURL, data => {
if (Array.isArray(data)) {
this.experiments = data;
return false;
}
this.experiments = [];
});
}, },
changeExperiment(value) { changeExperiment(value) {
this.selectedExperiment = value; this.selectedExperiment = value;
this.resetTaskSelector();
$.get(this.taskURL, data => {
if (Array.isArray(data)) {
this.tasks = data;
return false;
}
this.tasks = [];
});
}, },
changeTask(value) { changeTask(value) {
this.selectedTask = value; this.selectedTask = value;
},
resetProjectSelector() {
this.projects = [];
this.selectedProject = null;
},
resetExperimentSelector() {
this.experiments = [];
this.selectedExperiment = null;
},
resetTaskSelector() {
this.tasks = [];
this.selectedTask = null;
},
resetSelectors() {
this.resetTaskSelector();
this.resetExperimentSelector();
this.resetProjectSelector();
},
assign() {
if (!this.selectedTask) return;
$.ajax({
url: this.assignURL,
type: "PATCH",
dataType: "json",
data: { rows_to_assign: this.rowsToAssign }
}).always(() => {
this.resetSelectors();
this.deselectRows();
});
},
setShowCallback(callback) {
this.showCallback = callback;
},
deselectRows() {
$('.repository-row-selector:checked').trigger('click');
} }
} }
}; };

View file

@ -48,7 +48,7 @@
setTimeout(() => { setTimeout(() => {
this.isOpen = false; this.isOpen = false;
this.$emit('blur'); this.$emit('blur');
}, 100) }, 200)
}, },
toggle() { toggle() {
this.isOpen = !this.isOpen; this.isOpen = !this.isOpen;
@ -71,12 +71,11 @@
this.$emit('change', this.value); this.$emit('change', this.value);
}, },
updateOptionPosition() { updateOptionPosition() {
let rect = this.$refs.container.getBoundingClientRect(); const rect = this.$refs.container.getBoundingClientRect();
let top =rect.top + rect.height; const top = rect.height;
let left = rect.left; const width = rect.width;
let width = rect.width;
this.optionPositionStyle = `position: fixed; top: ${top}px; left: ${left}px; width: ${width}px` this.optionPositionStyle = `position: absolute; top: ${top}px; width: ${width}px`
} }
} }
} }

View file

@ -42,16 +42,20 @@
} else { } else {
this.currentOptions = this.options.filter((o) => o[1].toLowerCase().includes(this.query.toLowerCase())); this.currentOptions = this.options.filter((o) => o[1].toLowerCase().includes(this.query.toLowerCase()));
} }
},
options() {
this.currentOptions = this.options;
} }
}, },
computed: { computed: {
valueLabel() { valueLabel() {
let option = this.options.find((o) => o[0] === this.value); let option = this.currentOptions.find((o) => o[0] === this.value);
return option && option[1]; return option && option[1];
} }
}, },
methods: { methods: {
blur() { blur() {
this.isOpen = false;
this.$emit('blur'); this.$emit('blur');
}, },
change(value) { change(value) {
@ -68,7 +72,7 @@
this.$emit('close'); this.$emit('close');
}, },
fetchOptions() { fetchOptions() {
$.get(`/${this.optionsUrl}?query=${this.query}`, $.get(`${this.optionsUrl}?query=${this.query || ''}`,
(data) => { (data) => {
this.currentOptions = data; this.currentOptions = data;
} }

View file

@ -1,9 +1,9 @@
<div <div
class="assign-items-to-task-modal-container" class="assign-items-to-task-modal-container"
data-assign-url=<%= my_module_repository_path(MyModule.first) %> data-assign-url="<%= my_module_repository_path(":module_id") %>"
data-projects-url=<%= project_filter_projects_path %> data-projects-url="<%= project_filter_projects_path %>"
data-experiments-url=<%= experiment_filter_experiments_path %> data-experiments-url="<%= experiment_filter_experiments_path %>"
data-tasks-url=<%= module_filter_my_modules_path %> data-tasks-url="<%= module_filter_my_modules_path %>"
> >
<assign-items-to-task-modal-container <assign-items-to-task-modal-container
:visibility="visibility" :visibility="visibility"