mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-12-10 06:06:24 +08:00
Merge branch 'develop' into features/native-tables-revamp
This commit is contained in:
commit
dea9e2078f
56 changed files with 332 additions and 133 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.38.0.2
|
||||
1.39.0
|
||||
|
|
|
|||
|
|
@ -19,11 +19,14 @@
|
|||
});
|
||||
|
||||
function restoreMyModules(url, ids) {
|
||||
$.post(url, { my_modules_ids: ids, view: 'cards' });
|
||||
$.post(url, { my_modules_ids: ids, view: 'cards' }, () => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function initRestoreMyModules() {
|
||||
$('#module-archive').on('click', '#restoreTask', (e) => {
|
||||
$('#restoreTask').addClass('disable-click');
|
||||
e.stopPropagation();
|
||||
restoreMyModules(e.currentTarget.dataset.url, selectedTasks);
|
||||
});
|
||||
|
|
@ -46,6 +49,8 @@
|
|||
});
|
||||
|
||||
$('#modal-move-modules').on('click', 'button[data-action="confirm"]', () => {
|
||||
$('#modal-move-modules button[data-action="confirm"]').addClass('disable-click');
|
||||
|
||||
const moveParams = {
|
||||
to_experiment_id: $('#modal-move-modules').find('.selectpicker').val(),
|
||||
my_module_ids: selectedTasks
|
||||
|
|
@ -54,6 +59,7 @@
|
|||
HelperModule.flashAlertMsg(data.message, 'success');
|
||||
window.location.reload();
|
||||
}).fail((data) => {
|
||||
$('#modal-move-modules button[data-action="confirm"]').removeClass('disable-click');
|
||||
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
|
||||
});
|
||||
$('#modal-move-modules').modal('hide');
|
||||
|
|
|
|||
|
|
@ -731,10 +731,13 @@ var MyModuleRepositories = (function() {
|
|||
});
|
||||
|
||||
UPDATE_REPOSITORY_MODAL.on('click', '.downstream-action', function() {
|
||||
$(this).addClass('disable-click');
|
||||
submitUpdateRepositoryRecord({ downstream: true });
|
||||
}).on('click', '.task-action', function() {
|
||||
$(this).addClass('disable-click');
|
||||
submitUpdateRepositoryRecord({ downstream: false });
|
||||
}).on('hidden.bs.modal', function() {
|
||||
UPDATE_REPOSITORY_MODAL.find('.downstream-action, .task-action').removeClass('disable-click');
|
||||
FULL_VIEW_MODAL.focus();
|
||||
}).on('click', '.next-step', function() {
|
||||
UPDATE_REPOSITORY_MODAL.find('.next-step, .description-1, .rows-list-container').addClass('hidden');
|
||||
|
|
@ -763,7 +766,7 @@ var MyModuleRepositories = (function() {
|
|||
|
||||
function openAssignRecordsModal(downstream) {
|
||||
var assignUrl = FULL_VIEW_MODAL.data('assign-url-modal');
|
||||
$.get(assignUrl, { selected_rows: SELECTED_ROWS, downstream: downstream }, function(data) {
|
||||
$.get(assignUrl, { selected_rows: Object.keys(SELECTED_ROWS), downstream: downstream }, function(data) {
|
||||
UPDATE_REPOSITORY_MODAL.find('.modal-content').html(data.html);
|
||||
UPDATE_REPOSITORY_MODAL.data('update-url', data.update_url);
|
||||
UPDATE_REPOSITORY_MODAL.modal('show');
|
||||
|
|
|
|||
|
|
@ -476,10 +476,16 @@ function importProtocolFromFile(
|
|||
// Bind on "Import" buttons
|
||||
$('[data-action="import-current"]')
|
||||
.off('click')
|
||||
.on('click', function(ev) { importCurrentProtocol(ev); });
|
||||
.on('click', function(ev) {
|
||||
$(this).attr('disabled', true);
|
||||
importCurrentProtocol(ev);
|
||||
});
|
||||
$('[data-action="import-all"]')
|
||||
.off('click')
|
||||
.on('click', function(ev) { importAllProtocols(ev); });
|
||||
.on('click', function(ev) {
|
||||
$(this).attr('disabled', true);
|
||||
importAllProtocols(ev);
|
||||
});
|
||||
|
||||
// Bind on navigation buttons
|
||||
$('[data-action="jump-to-first-protocol"]')
|
||||
|
|
@ -520,6 +526,7 @@ function importProtocolFromFile(
|
|||
animateSpinner();
|
||||
importSingleProtocol(currentProtocol, false, function(data) {
|
||||
importModal.find('[data-role="preview-container"]').html('');
|
||||
importModal.find('[data-action]').removeAttr('disabled');
|
||||
afterImportCallback([data]);
|
||||
});
|
||||
} else {
|
||||
|
|
@ -529,6 +536,7 @@ function importProtocolFromFile(
|
|||
animateSpinner(importModal, false);
|
||||
importModal.find('[data-role="preview-container"]').html('');
|
||||
importModal.modal('hide');
|
||||
importModal.find('[data-action]').removeAttr('disabled');
|
||||
afterImportCallback([data]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ var protocolsIO = function() {
|
|||
function initFormSubmits() {
|
||||
var modal = $('#protocol-preview-modal');
|
||||
modal.find('button[data-action=import_protocol]').off('click').on('click', function() {
|
||||
$(this).attr('disabled', true);
|
||||
var form = modal.find('form');
|
||||
var hiddenField = form.find('#protocol_protocol_type');
|
||||
hiddenField.attr('value', $(this).data('import_type'));
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ var CommentsSidebar = (function() {
|
|||
|
||||
function initSendButton() {
|
||||
$(document).on('click', `${SIDEBAR} .send-comment, ${SIDEBAR} .update-comment`, function() {
|
||||
$(this).addClass('disable-click');
|
||||
|
||||
var requestUrl;
|
||||
var requestType;
|
||||
var updateMode = $(SIDEBAR).find('.sidebar-footer').hasClass('update');
|
||||
|
|
@ -94,9 +96,11 @@ var CommentsSidebar = (function() {
|
|||
}
|
||||
$('.error-container').empty();
|
||||
updateCounter();
|
||||
$(this).removeClass('disable-click');
|
||||
},
|
||||
error: (result) => {
|
||||
$('.error-container').text(result.responseJSON.errors.message);
|
||||
$(this).removeClass('disable-click');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -143,20 +143,27 @@
|
|||
$(document).on(
|
||||
'ajax:success',
|
||||
"[data-id='destroy-user-team-form']",
|
||||
function() {
|
||||
// Hide modal & clear its contents
|
||||
var modal = $('#destroy-user-team-modal');
|
||||
var modalHeading = modal.find('.modal-header').find('.modal-title');
|
||||
var modalBody = modal.find('.modal-body');
|
||||
modalHeading.text('');
|
||||
modalBody.html('');
|
||||
function(_e, jobData, _status, _xhr) {
|
||||
// Wait for removal job to complete
|
||||
var jobStatusInterval = setInterval(() => {
|
||||
$.get(`/jobs/${jobData.job_id}/status`, function(data) {
|
||||
if (data.status === 'done') {
|
||||
// Reload the whole table
|
||||
HelperModule.flashAlertMsg(jobData.success_message, 'success');
|
||||
usersDatatable.ajax.reload();
|
||||
animateSpinner(null, false);
|
||||
$('#destroy-user-team-modal').modal('hide');
|
||||
clearInterval(jobStatusInterval);
|
||||
}
|
||||
|
||||
// Hide the modal
|
||||
modal.modal('hide');
|
||||
|
||||
animateSpinner(null, false);
|
||||
// Reload the whole table
|
||||
location.reload();
|
||||
if (data.status === 'failed') {
|
||||
HelperModule.flashAlertMsg(I18n.t('users.settings.user_teams.general_error'), 'danger');
|
||||
animateSpinner(null, false);
|
||||
$('#destroy-user-team-modal').modal('hide');
|
||||
clearInterval(jobStatusInterval);
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
).on(
|
||||
'ajax:error',
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class LabelTemplatesController < ApplicationController
|
|||
LabelTemplate.where(team_id: current_team.id, id: params[:selected_ids]).each do |template|
|
||||
new_template = template.dup
|
||||
new_template.default = false
|
||||
new_template.predefined = false
|
||||
new_template.created_by = current_user
|
||||
new_template.last_modified_by = current_user
|
||||
new_template.name = template.name + '(1)'
|
||||
|
|
@ -170,7 +171,7 @@ class LabelTemplatesController < ApplicationController
|
|||
end
|
||||
|
||||
def load_label_templates
|
||||
@label_templates = LabelTemplate.enabled? ? current_team.label_templates : current_team.label_templates.default
|
||||
@label_templates = LabelTemplate.enabled? ? current_team.label_templates : current_team.label_templates.predefined
|
||||
end
|
||||
|
||||
def load_label_template
|
||||
|
|
|
|||
|
|
@ -76,9 +76,10 @@ class ResultsController < ApplicationController
|
|||
end
|
||||
|
||||
def assets
|
||||
render json: @result.assets,
|
||||
render json: @result.assets.preload(:preview_image_attachment, file_attachment: :blob, result: { my_module: { experiment: :project, user_assignments: %i(user user_role) } }),
|
||||
each_serializer: AssetSerializer,
|
||||
user: current_user
|
||||
user: current_user,
|
||||
managable_result: can_manage_result?(@result)
|
||||
end
|
||||
|
||||
def upload_attachment
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ class StepsController < ApplicationController
|
|||
end
|
||||
|
||||
def attachments
|
||||
render json: @step.assets,
|
||||
render json: @step.assets.preload(:preview_image_attachment, file_attachment: :blob,
|
||||
step: { protocol: { my_module: { experiment: :project, user_assignments: %i(user user_role) } } }),
|
||||
each_serializer: AssetSerializer,
|
||||
user: current_user
|
||||
user: current_user,
|
||||
managable_step: can_manage_step?(@step)
|
||||
end
|
||||
|
||||
def upload_attachment
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ module Users
|
|||
# If user is last administrator of team,
|
||||
# he/she cannot be deleted from it.
|
||||
invalid = @user_assignment.last_with_permission?(TeamPermissions::USERS_MANAGE)
|
||||
job_id = nil
|
||||
|
||||
unless invalid
|
||||
begin
|
||||
|
|
@ -84,7 +85,7 @@ module Users
|
|||
})
|
||||
end
|
||||
reset_user_current_team(@user_assignment)
|
||||
@user_assignment.destroy!
|
||||
job_id = UserAssignments::RemoveTeamUserAssignmentsJob.perform_later(@user_assignment).job_id
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.error e.message
|
||||
|
|
@ -95,7 +96,7 @@ module Users
|
|||
if invalid
|
||||
render json: @user_assignment.errors, status: :unprocessable_entity
|
||||
else
|
||||
flash[:success] = if params[:leave]
|
||||
success_message = if params[:leave]
|
||||
I18n.t(
|
||||
'users.settings.user_teams.leave_flash',
|
||||
team: @user_assignment.assignable.name
|
||||
|
|
@ -112,7 +113,7 @@ module Users
|
|||
@user_assignment.user,
|
||||
@user_assignment.assignable,
|
||||
false)
|
||||
render json: { status: :ok }
|
||||
render json: { status: :ok, job_id: job_id, success_message: success_message }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ class WopiController < ActionController::Base
|
|||
@can_write = can_manage_step?(@assoc)
|
||||
@close_url = protocols_url(only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
|
||||
@breadcrump_brand_name = 'Projects'
|
||||
@breadcrumb_brand_name = @protocol.name
|
||||
@breadcrumb_brand_url = root_url(only_path: false, host: ENV['WOPI_USER_HOST'])
|
||||
@breadcrumb_folder_name = 'Protocol managament'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
:class="{ 'sn-action-toolbar--button-overflow': buttonOverflow }"
|
||||
:style="`width: ${width}px; bottom: ${bottomOffset}px; transform: translateX(${leftOffset}px)`"
|
||||
:data-e2e="`e2e-CO-actionToolbar`">
|
||||
<div class="sn-action-toolbar__actions flex gap-4">
|
||||
<div class="sn-action-toolbar__actions flex gap-4" :class="{ 'disable-click': submitting }">
|
||||
<div v-if="loading && !actions.length" class="sn-action-toolbar__action">
|
||||
<a class="rounded flex items-center py-1.5 px-2.5 bg-transparent text-transparent no-underline"></a>
|
||||
</div>
|
||||
|
|
@ -94,7 +94,8 @@ export default {
|
|||
width: 0,
|
||||
bottomOffset: 0,
|
||||
leftOffset: 0,
|
||||
buttonOverflow: false
|
||||
buttonOverflow: false,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
|
@ -185,6 +186,8 @@ export default {
|
|||
break;
|
||||
case 'request':
|
||||
event.stopPropagation();
|
||||
this.submitting = true;
|
||||
|
||||
$.ajax({
|
||||
type: action.request_method,
|
||||
url: action.path,
|
||||
|
|
@ -194,6 +197,7 @@ export default {
|
|||
}).fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON && data.responseJSON.message || data.message, 'danger');
|
||||
}).always(() => {
|
||||
this.submitting = false;
|
||||
if (this.reloadCallback) this.reloadCallback();
|
||||
});
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" :disabled="!targetProject" type="submit">
|
||||
<button class="btn btn-primary" :disabled="submitting || !targetProject" type="submit">
|
||||
{{ i18n.t('experiments.clone.modal_submit') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -53,10 +53,13 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
targetProject: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.experiment.urls.clone, {
|
||||
experiment: {
|
||||
project_id: this.targetProject,
|
||||
|
|
@ -65,6 +68,7 @@ export default {
|
|||
this.$emit('update');
|
||||
window.location.replace(response.data.url);
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button type="submit" :disabled="!validName" class="btn btn-primary">
|
||||
<button type="submit" :disabled="submitting || !validName" class="btn btn-primary">
|
||||
{{ i18n.t('experiments.edit.modal_create') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -55,6 +55,7 @@ export default {
|
|||
return {
|
||||
name: this.experiment.name,
|
||||
description: this.experiment.description,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -69,6 +70,8 @@ export default {
|
|||
mixins: [modalMixin],
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.patch(this.experiment.urls.update, {
|
||||
experiment: {
|
||||
name: this.name,
|
||||
|
|
@ -76,8 +79,10 @@ export default {
|
|||
},
|
||||
}).then((response) => {
|
||||
this.$emit('update');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" :disabled="!targetProject || disableSubmit" type="submit">
|
||||
<button class="btn btn-primary" :disabled="!targetProject || submitting" type="submit">
|
||||
{{ i18n.t('experiments.move.modal_submit') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -49,12 +49,13 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
targetProject: null,
|
||||
disableSubmit: false
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
this.disableSubmit = true;
|
||||
this.submitting = true;
|
||||
|
||||
await axios.post(this.experiment.movePath, {
|
||||
project_id: this.targetProject
|
||||
}).then((response) => {
|
||||
|
|
@ -63,7 +64,7 @@ export default {
|
|||
}).catch((error) => {
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
this.disableSubmit = false;
|
||||
this.submitting = false;
|
||||
},
|
||||
changeProject(project) {
|
||||
this.targetProject = project;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button type="submit" :disabled="!validName" class="btn btn-primary">
|
||||
<button type="submit" :disabled="submitting || !validName" class="btn btn-primary">
|
||||
{{ i18n.t('experiments.new.modal_create') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -54,7 +54,8 @@ export default {
|
|||
return {
|
||||
name: '',
|
||||
description: '',
|
||||
error: null
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -68,6 +69,8 @@ export default {
|
|||
mixins: [modalMixin],
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.createUrl, {
|
||||
experiment: {
|
||||
name: this.name,
|
||||
|
|
@ -77,6 +80,7 @@ export default {
|
|||
this.$emit('create');
|
||||
window.location.replace(response.data.path);
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.name[0];
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -99,7 +99,8 @@ export default {
|
|||
headerName: this.i18n.t('label_templates.index.created_at'),
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
],
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -133,41 +134,70 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
setDefault(action) {
|
||||
if (this.submitting) return;
|
||||
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(action.path).then((response) => {
|
||||
this.reloadingTable = true;
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
},
|
||||
duplicate(action, rows) {
|
||||
if (this.submitting) return;
|
||||
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(action.path, { selected_ids: rows.map((row) => row.id) }).then((response) => {
|
||||
this.reloadingTable = true;
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
},
|
||||
createTemplate(action) {
|
||||
if (this.submitting) return;
|
||||
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(action.path).then((response) => {
|
||||
window.location.href = response.data.redirect_url;
|
||||
});
|
||||
},
|
||||
syncFluicsLabels(action) {
|
||||
if (this.submitting) return;
|
||||
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(action.path).then((response) => {
|
||||
this.reloadingTable = true;
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
},
|
||||
async deleteTemplates(action, rows) {
|
||||
if (this.submitting) return;
|
||||
|
||||
const ok = await this.$refs.deleteModal.show();
|
||||
|
||||
if (ok) {
|
||||
this.submitting = true;
|
||||
|
||||
axios.delete(action.path, { data: { selected_ids: rows.map((row) => row.id) } }).then((response) => {
|
||||
this.reloadingTable = true;
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button type="submit" :disabled="!nameIsValid" class="btn btn-primary">
|
||||
<button type="submit" :disabled="submitting || !nameIsValid" class="btn btn-primary">
|
||||
{{ i18n.t('experiments.canvas.edit.modal_edit_module.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -44,7 +44,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
name: this.my_module.name
|
||||
name: this.my_module.name,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -55,13 +56,17 @@ export default {
|
|||
mixins: [modalMixin],
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.patch(this.my_module.urls.update, {
|
||||
my_module: {
|
||||
name: this.name
|
||||
}
|
||||
}).then(() => {
|
||||
this.$emit('update');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" :disabled="!targetExperiment" type="submit">
|
||||
<button class="btn btn-primary" :disabled="submitting || !targetExperiment" type="submit">
|
||||
{{ i18n.t('experiments.table.modal_move_modules.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -47,17 +47,22 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
targetExperiment: null
|
||||
targetExperiment: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.my_module.movePath, {
|
||||
to_experiment_id: this.targetExperiment
|
||||
}).then((response) => {
|
||||
this.$emit('move');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button type="submit" :disabled="!validName" class="btn btn-primary">
|
||||
<button type="submit" :disabled="submitting || !validName" class="btn btn-primary">
|
||||
{{ i18n.t('experiments.canvas.new_my_module_modal.create') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -100,7 +100,8 @@ export default {
|
|||
tags: [],
|
||||
users: [],
|
||||
allTags: [],
|
||||
allUsers: []
|
||||
allUsers: [],
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -135,6 +136,8 @@ export default {
|
|||
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.createUrl, {
|
||||
my_module: {
|
||||
name: this.name,
|
||||
|
|
@ -144,7 +147,9 @@ export default {
|
|||
}
|
||||
}).then(() => {
|
||||
this.$emit('create');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" :disabled="visible && !defaultRole">
|
||||
<button class="btn btn-primary" type="submit" :disabled="visible && !defaultRole || submitting">
|
||||
{{ i18n.t('projects.index.modal_edit_project.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -78,11 +78,13 @@ export default {
|
|||
visible: !this.project.hidden,
|
||||
defaultRole: this.project.default_public_user_role_id,
|
||||
error: null,
|
||||
userRoles: []
|
||||
userRoles: [],
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
axios.put(this.project.urls.update, {
|
||||
project: {
|
||||
name: this.name,
|
||||
|
|
@ -92,7 +94,9 @@ export default {
|
|||
}).then(() => {
|
||||
this.error = null;
|
||||
this.$emit('update');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.errors.name;
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting">
|
||||
{{ i18n.t('projects.index.modal_edit_folder.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -52,10 +52,12 @@ export default {
|
|||
return {
|
||||
name: this.folder.name,
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
axios.put(this.folder.urls.update, {
|
||||
project_folder: {
|
||||
name: this.name,
|
||||
|
|
@ -63,7 +65,9 @@ export default {
|
|||
}).then(() => {
|
||||
this.error = null;
|
||||
this.$emit('update');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.errors.name;
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting">
|
||||
{{ i18n.t('projects.index.modal_move_folder.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -66,6 +66,7 @@ export default {
|
|||
selectedFolderId: null,
|
||||
foldersTree: [],
|
||||
query: '',
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
|
@ -115,6 +116,7 @@ export default {
|
|||
this.selectedFolderId = folderId;
|
||||
},
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
axios.post(this.moveToUrl, {
|
||||
destination_folder_id: this.selectedFolderId || 'root_folder',
|
||||
movables: this.selectedObjects.map((obj) => (
|
||||
|
|
@ -125,8 +127,10 @@ export default {
|
|||
)),
|
||||
}).then((response) => {
|
||||
this.$emit('move');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.message, 'danger');
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" :disabled="disableSubmit || (visible && !defaultRole) || !validName ">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting || (visible && !defaultRole) || !validName">
|
||||
{{ i18n.t('projects.index.modal_new_project.create') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -87,13 +87,14 @@ export default {
|
|||
visible: false,
|
||||
defaultRole: null,
|
||||
error: null,
|
||||
disableSubmit: false,
|
||||
submitting: false,
|
||||
userRoles: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
this.disableSubmit = true;
|
||||
this.submitting = true;
|
||||
|
||||
await axios.post(this.createUrl, {
|
||||
project: {
|
||||
name: this.name,
|
||||
|
|
@ -107,7 +108,7 @@ export default {
|
|||
}).catch((error) => {
|
||||
this.error = error.response.data.name;
|
||||
});
|
||||
this.disableSubmit = false;
|
||||
this.submitting = false;
|
||||
},
|
||||
changeRole(role) {
|
||||
this.defaultRole = role;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting">
|
||||
{{ i18n.t('projects.index.modal_new_project.create') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -55,10 +55,12 @@ export default {
|
|||
return {
|
||||
name: '',
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
axios.post(this.createFolderUrl, {
|
||||
project_folder: {
|
||||
name: this.name,
|
||||
|
|
@ -68,8 +70,10 @@ export default {
|
|||
}).then(() => {
|
||||
this.error = null;
|
||||
this.$emit('create');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.error = error.response.data.name;
|
||||
this.submitting = false;
|
||||
});
|
||||
},
|
||||
changeRole(role) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="cancel" data-e2e="e2e-BT-publishProtocol-cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" @click="confirm" data-e2e="e2e-BT-publishProtocol-publish">{{ i18n.t('protocols.publish_modal.publish')}}</button>
|
||||
<button class="btn btn-primary" @click="confirm" :disabled="submitting" data-e2e="e2e-BT-publishProtocol-publish">{{ i18n.t('protocols.publish_modal.publish')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -40,6 +40,11 @@ export default {
|
|||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
|
|
@ -51,6 +56,7 @@ export default {
|
|||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('publish', this.protocol.attributes.version_comment);
|
||||
this.submitting = true;
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
<div class="modal-footer">
|
||||
<button v-if="state === 'confirm'" type="button"
|
||||
class="btn btn-primary"
|
||||
@click.stop.prevent="confirm">{{ i18n.t('protocols.import_modal.import') }}</button>
|
||||
@click.stop.prevent="confirm"
|
||||
:disabled="submitting">{{ i18n.t('protocols.import_modal.import') }}</button>
|
||||
<button v-else-if="state === 'failed'" type="button"
|
||||
class="btn btn-primary"
|
||||
data-dismiss="modal">{{ i18n.t('protocols.import_modal.close') }}</button>
|
||||
|
|
@ -41,7 +42,8 @@ export default {
|
|||
jobPollInterval: null,
|
||||
pollCount: 0,
|
||||
jobId: null,
|
||||
refreshCallback: null
|
||||
refreshCallback: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -69,6 +71,7 @@ export default {
|
|||
confirm() {
|
||||
const formData = new FormData();
|
||||
Array.from(this.files).forEach((file) => formData.append('files[]', file, file.name));
|
||||
this.submitting = true;
|
||||
|
||||
$.post({
|
||||
url: this.importUrl, data: formData, processData: false, contentType: false
|
||||
|
|
@ -97,10 +100,12 @@ export default {
|
|||
break;
|
||||
case 'done':
|
||||
this.state = 'done';
|
||||
this.submitting = false;
|
||||
clearInterval(this.jobPollInterval);
|
||||
break;
|
||||
case 'failed':
|
||||
this.state = 'failed';
|
||||
this.submitting = false;
|
||||
clearInterval(this.jobPollInterval);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" data-e2e="e2e-BT-newProtocolModal-cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" :disabled="(visible && !defaultRole) || !validName" data-e2e="e2e-BT-newProtocolModal-create">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting || (visible && !defaultRole) || !validName" data-e2e="e2e-BT-newProtocolModal-create">
|
||||
{{ i18n.t('protocols.new_protocol_modal.create_new') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -91,11 +91,14 @@ export default {
|
|||
visible: false,
|
||||
defaultRole: null,
|
||||
userRoles: [],
|
||||
error: null
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.createUrl, {
|
||||
protocol: {
|
||||
name: this.name,
|
||||
|
|
@ -105,7 +108,9 @@ export default {
|
|||
}).then(() => {
|
||||
this.error = null;
|
||||
this.$emit('create');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.error;
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" data-e2e="e2e-BT-duplicateInventoryModal-cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" data-e2e="e2e-BT-duplicateInventoryModal-create">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting" data-e2e="e2e-BT-duplicateInventoryModal-create">
|
||||
{{ i18n.t('repositories.index.modal_copy.copy') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -49,11 +49,14 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
name: this.repository.name,
|
||||
error: null
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.repository.urls.duplicate, {
|
||||
repository: {
|
||||
name: this.name
|
||||
|
|
@ -61,8 +64,10 @@ export default {
|
|||
}).then((response) => {
|
||||
this.error = null;
|
||||
this.$emit('duplicate');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.name;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" data-e2e="e2e-BT-renameInventoryModal-cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" data-e2e="e2e-BT-renameInventoryModal-save">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting" data-e2e="e2e-BT-renameInventoryModal-save">
|
||||
{{ i18n.t('repositories.index.modal_rename.rename') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -49,11 +49,14 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
name: this.repository.name,
|
||||
error: null
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.put(this.repository.urls.update, {
|
||||
repository: {
|
||||
name: this.name
|
||||
|
|
@ -61,7 +64,9 @@ export default {
|
|||
}).then(() => {
|
||||
this.error = null;
|
||||
this.$emit('update');
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.name;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button type="submit" class="btn btn-primary"> {{ i18n.t('repositories.index.modal_export.export') }} </button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="submitting"> {{ i18n.t('repositories.index.modal_export.export') }} </button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -48,7 +48,8 @@ export default {
|
|||
mixins: [modalMixin],
|
||||
data() {
|
||||
return {
|
||||
selectedOption: this.exportAction.export_file_type
|
||||
selectedOption: this.exportAction.export_file_type,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -58,10 +59,14 @@ export default {
|
|||
file_type: this.selectedOption
|
||||
};
|
||||
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.exportAction.path, payload).then((response) => {
|
||||
this.$emit('export');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" data-e2e="e2e-BT-newInventoryModal-cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" :disabled="!validName" data-e2e="e2e-BT-newInventoryModal-create">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting || !validName" data-e2e="e2e-BT-newInventoryModal-create">
|
||||
{{ i18n.t('repositories.index.modal_create.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -50,7 +50,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
name: '',
|
||||
error: null
|
||||
error: null,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -60,6 +61,8 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.createUrl, {
|
||||
repository: {
|
||||
name: this.name
|
||||
|
|
@ -67,8 +70,10 @@ export default {
|
|||
}).then((response) => {
|
||||
this.error = null;
|
||||
this.$emit('create');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
this.error = error.response.data.name;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"> {{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" @click="submitPrint">
|
||||
<button class="btn btn-primary" @click="submitPrint" :disabled="submitting">
|
||||
{{ i18n.t(`repository_row.modal_print_label.${labelTemplateError ? 'print_anyway' : 'print_label'}`) }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -114,7 +114,8 @@ export default {
|
|||
zebraPrinters: null,
|
||||
labelTemplateError: null,
|
||||
labelTemplateCode: null,
|
||||
fetchedPrintersAndTemplates: false
|
||||
fetchedPrintersAndTemplates: false,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
|
@ -223,6 +224,8 @@ export default {
|
|||
});
|
||||
},
|
||||
submitPrint() {
|
||||
this.submitting = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.selectedPrinter.attributes.type_of === 'zebra') {
|
||||
this.zebraPrinters.print(
|
||||
|
|
@ -247,8 +250,10 @@ export default {
|
|||
}, (data) => {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('close');
|
||||
this.submitting = false;
|
||||
PrintProgressModal.init(data);
|
||||
}).fail(() => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(this.i18n.t('repository_row.modal_print_label.general_error'), 'danger');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="p-4 w-full rounded bg-sn-light-grey min-h-[68px]" data-e2e="e2e-CO-actionToolbar">
|
||||
<div class="p-4 w-full rounded bg-sn-light-grey min-h-[68px]" :class="{ 'disable-click': submitting }" data-e2e="e2e-CO-actionToolbar">
|
||||
<div class="flex gap-4 items-center h-full">
|
||||
<div v-if="loading && !actions.length" class="sn-action-toolbar__action">
|
||||
<a class="rounded flex items-center py-1.5 px-2.5 bg-transparent text-transparent no-underline"></a>
|
||||
|
|
@ -42,6 +42,7 @@ export default {
|
|||
reloadCallback: null,
|
||||
loaded: false,
|
||||
loading: true,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -72,8 +73,8 @@ export default {
|
|||
switch (action.type) {
|
||||
case 'emit':
|
||||
event.preventDefault();
|
||||
this.submitting = true;
|
||||
this.$emit('toolbar:action', action);
|
||||
// do nothing, this is handled by legacy code based on the button class
|
||||
break;
|
||||
case 'modal':
|
||||
// do nothihg, boostrap modal handled by data-toggle="modal" and data-target
|
||||
|
|
|
|||
|
|
@ -180,10 +180,11 @@ export default {
|
|||
type: Object
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.searchValue.length > 0) {
|
||||
this.openSearch();
|
||||
}
|
||||
data() {
|
||||
return {
|
||||
showSearch: false,
|
||||
showColumnsModal: false
|
||||
};
|
||||
},
|
||||
components: {
|
||||
MenuDropdown,
|
||||
|
|
@ -239,12 +240,6 @@ export default {
|
|||
return this.columnDefs.filter((column) => column.sortable);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSearch: false,
|
||||
showColumnsModal: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
searchValue() {
|
||||
if (this.searchValue.length > 0) {
|
||||
|
|
@ -252,6 +247,11 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.searchValue.length > 0) {
|
||||
this.openSearch();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSearch() {
|
||||
this.showSearch = true;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" type="submit" :disabled="!validObject">
|
||||
<button class="btn btn-primary" type="submit" :disabled="submitting || !validObject">
|
||||
{{ i18n.t(`storage_locations.show.assign_modal.${assignMode}_action`) }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -102,7 +102,7 @@ export default {
|
|||
rowId: this.selectedRow,
|
||||
containerId: this.selectedContainer,
|
||||
position: this.selectedPosition,
|
||||
saving: false
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
|
@ -112,21 +112,21 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
submit() {
|
||||
if (this.saving) {
|
||||
if (this.submitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.saving = true;
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.actionUrl, {
|
||||
repository_row_id: this.rowId,
|
||||
metadata: { position: this.position?.map((pos) => parseInt(pos, 10)) }
|
||||
}).then(() => {
|
||||
this.$emit('close');
|
||||
this.saving = false;
|
||||
this.submitting = false;
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.errors.join(', '), 'danger');
|
||||
this.saving = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" :disabled="!validContainer" type="submit">
|
||||
<button class="btn btn-primary" :disabled="submitting || !validContainer" type="submit">
|
||||
{{ i18n.t('general.move') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -81,17 +81,22 @@ export default {
|
|||
storageLocationsTree: [],
|
||||
teamId: null,
|
||||
query: '',
|
||||
moveMode: 'locations'
|
||||
moveMode: 'locations',
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.submitting = true;
|
||||
|
||||
axios.post(this.moveToUrl, {
|
||||
destination_storage_location_id: this.selectedStorageLocationId || 'root_storage_location'
|
||||
}).then((response) => {
|
||||
this.$emit('move');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(response.data.message, 'success');
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" :disabled="!validObject" type="submit">
|
||||
<button class="btn btn-primary" :disabled="submitting || !validObject" type="submit">
|
||||
{{ mode == 'create' ? i18n.t('general.create') : i18n.t('general.save') }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -127,7 +127,7 @@ export default {
|
|||
},
|
||||
attachedImage: null,
|
||||
imageError: false,
|
||||
savingLocaiton: false,
|
||||
submitting: false,
|
||||
errors: {}
|
||||
};
|
||||
},
|
||||
|
|
@ -187,11 +187,11 @@ export default {
|
|||
// Smart annotation fix
|
||||
this.object.description = $(this.$refs.description).val();
|
||||
|
||||
if (this.savingLocaiton) {
|
||||
if (this.submitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.savingLocaiton = true;
|
||||
this.submitting = true;
|
||||
|
||||
const params = {
|
||||
name: this.object.name,
|
||||
|
|
@ -214,22 +214,22 @@ export default {
|
|||
.then(() => {
|
||||
this.$emit('tableReloaded');
|
||||
HelperModule.flashAlertMsg(this.i18n.t(`storage_locations.index.edit_modal.success_message.edit_${this.editModalMode}`, { name: this.object.name }), 'success');
|
||||
this.savingLocaiton = false;
|
||||
this.submitting = false;
|
||||
this.close();
|
||||
}).catch((error) => {
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
this.savingLocaiton = false;
|
||||
this.submitting = false;
|
||||
});
|
||||
} else {
|
||||
axios.post(this.createUrl, params)
|
||||
.then(() => {
|
||||
this.$emit('tableReloaded');
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(this.i18n.t(`storage_locations.index.edit_modal.success_message.create_${this.editModalMode}`, { name: this.object.name }), 'success');
|
||||
this.savingLocaiton = false;
|
||||
this.close();
|
||||
}).catch((error) => {
|
||||
this.submitting = false;
|
||||
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
|
||||
this.savingLocaiton = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ module UserAssignments
|
|||
def perform(team_user_assignment)
|
||||
ActiveRecord::Base.transaction do
|
||||
RemoveTeamUserAssignmentsService.new(team_user_assignment).call
|
||||
team_user_assignment.destroy!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -349,8 +349,8 @@ class Asset < ApplicationRecord
|
|||
end
|
||||
|
||||
def unlock_expired
|
||||
with_lock do
|
||||
if !lock_ttl.nil? && lock_ttl < Time.now.to_i
|
||||
if !lock_ttl.nil? && lock_ttl < Time.now.to_i
|
||||
with_lock do
|
||||
self.lock = nil
|
||||
self.lock_ttl = nil
|
||||
save!
|
||||
|
|
@ -377,7 +377,7 @@ class Asset < ApplicationRecord
|
|||
end
|
||||
|
||||
def editable_image?
|
||||
!locked? && (%r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES_EDITABLE)}} =~ file.content_type).present?
|
||||
(%r{^image/#{Regexp.union(Constants::WHITELISTED_IMAGE_TYPES_EDITABLE)}} =~ file.content_type).present?
|
||||
end
|
||||
|
||||
def generate_base64(style)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Comment < ApplicationRecord
|
||||
include SearchableModel
|
||||
|
||||
|
|
@ -5,21 +7,15 @@ class Comment < ApplicationRecord
|
|||
validates :message,
|
||||
presence: true,
|
||||
length: { maximum: Constants::TEXT_MAX_LENGTH }
|
||||
validates :user, presence: true
|
||||
|
||||
belongs_to :user, inverse_of: :comments
|
||||
belongs_to :last_modified_by, foreign_key: 'last_modified_by_id', class_name: 'User', optional: true
|
||||
belongs_to :last_modified_by, class_name: 'User', inverse_of: :comments, optional: true
|
||||
|
||||
scope :unseen_by, ->(user) { where('? = ANY (unseen_by)', user.id) }
|
||||
|
||||
def self.mark_as_seen_by(user, commentable)
|
||||
def self.mark_as_seen_by(user)
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
all.where('? = ANY (unseen_by)', user.id).update_all("unseen_by = array_remove(unseen_by, #{user.id.to_i}::bigint)")
|
||||
|
||||
# Because we want the number of unseen comments to affect the cache of project
|
||||
# and experiment lists, we need to set the updated_at of Project or Experiment.
|
||||
commentable.touch if commentable.class.in? [Project, Experiment]
|
||||
|
||||
where('? = ANY (unseen_by)', user.id).update_all("unseen_by = array_remove(unseen_by, #{user.id.to_i}::bigint)")
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ class FluicsLabelTemplate < LabelTemplate
|
|||
height_mm: 12.7,
|
||||
content: Extends::DEFAULT_LABEL_TEMPLATE[:zpl],
|
||||
unit: 0,
|
||||
density: 12
|
||||
density: 12,
|
||||
predefined: true
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class LabelTemplate < ApplicationRecord
|
|||
validate :ensure_single_default_template!
|
||||
|
||||
scope :default, -> { where(default: true) }
|
||||
scope :predefined, -> { where(predefined: true) }
|
||||
|
||||
def self.viewable_by_user(user, teams)
|
||||
joins("INNER JOIN user_assignments team_user_assignments
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ class Token < ApplicationRecord
|
|||
validates :token, presence: true
|
||||
validates :ttl, presence: true
|
||||
|
||||
belongs_to :user, foreign_key: 'user_id', class_name: 'User', inverse_of: :tokens
|
||||
belongs_to :user, inverse_of: :tokens
|
||||
end
|
||||
|
|
|
|||
|
|
@ -461,23 +461,17 @@ class User < ApplicationRecord
|
|||
Rails.logger.warn "WOPI: searching by token #{token}"
|
||||
User.joins(:tokens)
|
||||
.where(tokens: { token: token })
|
||||
.find_by('tokens.ttl = 0 OR tokens.ttl > ?', Time.now.to_i)
|
||||
.find_by('tokens.ttl > ?', Time.now.utc.to_i)
|
||||
end
|
||||
|
||||
def get_wopi_token
|
||||
# WOPI does not have a good way to request a new token,
|
||||
# so a new token should be provided each time this is called,
|
||||
# while keeping any old tokens as long as they have not yet expired
|
||||
tokens = Token.where(user_id: id).distinct
|
||||
|
||||
tokens.each do |token|
|
||||
token.delete if token.ttl < Time.now.to_i
|
||||
end
|
||||
|
||||
token_string = "#{Devise.friendly_token(20)}-#{id}"
|
||||
# WOPI uses millisecond TTLs
|
||||
ttl = (Time.now + 1.day).to_i
|
||||
wopi_token = Token.create(token: token_string, ttl: ttl, user_id: id)
|
||||
token_string = "#{Devise.friendly_token(20)}-#{id}"
|
||||
# WOPI uses millisecond TTLs and 10 hours TTL is recommended by the vendor
|
||||
ttl = Time.now.utc.to_i + Constants::WOPI_TOKEN_VALIDITY
|
||||
wopi_token = tokens.create!(token: token_string, ttl: ttl)
|
||||
Rails.logger.warn("WOPI: generating new token #{wopi_token.token}")
|
||||
wopi_token
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ class ZebraLabelTemplate < LabelTemplate
|
|||
height_mm: 12.7,
|
||||
content: Extends::DEFAULT_LABEL_TEMPLATE[:zpl],
|
||||
unit: 0,
|
||||
density: 12
|
||||
density: 12,
|
||||
predefined: true
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -19,7 +20,8 @@ class ZebraLabelTemplate < LabelTemplate
|
|||
height_mm: 12.7,
|
||||
content: Extends::DEFAULT_LABEL_TEMPLATE_203DPI[:zpl],
|
||||
unit: 0,
|
||||
density: 12
|
||||
density: 12,
|
||||
predefined: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class AssetSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def pdf_previewable
|
||||
object.pdf_previewable? if object.file.attached?
|
||||
object.pdf_previewable?
|
||||
end
|
||||
|
||||
def pdf
|
||||
|
|
@ -100,7 +100,7 @@ class AssetSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def image_editable
|
||||
object.editable_image?
|
||||
@image_editable ||= object.editable_image?
|
||||
end
|
||||
|
||||
def checksum
|
||||
|
|
@ -142,7 +142,7 @@ class AssetSerializer < ActiveModel::Serializer
|
|||
versions: (asset_versions_path(object) if attached)
|
||||
}
|
||||
user = scope[:user] || @instance_options[:user]
|
||||
if can_manage_asset?(user, object)
|
||||
if managable?
|
||||
urls.merge!(
|
||||
toggle_view_mode: toggle_view_mode_path(object),
|
||||
edit_asset: edit_asset_path(object),
|
||||
|
|
@ -157,9 +157,9 @@ class AssetSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
urls[:restore_version] = asset_restore_version_path(object) if can_restore_asset?(user, object)
|
||||
urls[:open_vector_editor_edit] = edit_gene_sequence_asset_path(object.id) if can_manage_asset?(user, object)
|
||||
urls[:open_vector_editor_edit] = edit_gene_sequence_asset_path(object.id) if managable?
|
||||
|
||||
if can_manage_asset?(user, object) && can_open_asset_locally?(user, object)
|
||||
if managable? && can_open_asset_locally?(user, object)
|
||||
urls[:open_locally] = asset_sync_show_path(object)
|
||||
urls[:open_locally_api] = Constants::ASSET_SYNC_URL
|
||||
urls[:asset_show] = asset_show_path(object)
|
||||
|
|
@ -176,4 +176,13 @@ class AssetSerializer < ActiveModel::Serializer
|
|||
user = scope[:user] || @instance_options[:user]
|
||||
can_open_asset_locally?(user, object)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def managable?
|
||||
return true if @instance_options[:managable_step] || @instance_options[:managable_result]
|
||||
|
||||
user = scope[:user] || @instance_options[:user]
|
||||
@managable ||= can_manage_asset?(user, object)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
module ActiveStorageFileUtil
|
||||
# Method expects instance of ActiveStorage::Blob as argument
|
||||
def previewable_document?(blob)
|
||||
return false if blob.blank?
|
||||
|
||||
previewable = Constants::PREVIEWABLE_FILE_TYPES.include?(blob.content_type)
|
||||
|
||||
file_extension = blob.filename.extension
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
<div class="modal-body">
|
||||
<p>
|
||||
<% if downstream %>
|
||||
<%= t('my_modules.modals.assign_repository_record.message_downstream', size: selected_rows.values.count) %>
|
||||
<%= t('my_modules.modals.assign_repository_record.message_downstream', size: selected_rows.count) %>
|
||||
<% else %>
|
||||
<%= t('my_modules.modals.assign_repository_record.message', size: selected_rows.values.count) %>
|
||||
<%= t('my_modules.modals.assign_repository_record.message', size: selected_rows.count) %>
|
||||
<% end %>
|
||||
</p>
|
||||
<% if downstream %>
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@
|
|||
locals: { comment: comment, skip_header: user == comment.user } %>
|
||||
<% user = comment.user %>
|
||||
<% end %>
|
||||
<% comments.mark_as_seen_by(current_user, @commentable) %>
|
||||
<% comments.mark_as_seen_by(current_user) %>
|
||||
|
|
|
|||
|
|
@ -309,6 +309,8 @@ class Constants
|
|||
docx docm odt xlsx xlsm xlsb ods pptx ppsx odp wopitest wopitestx
|
||||
).freeze
|
||||
|
||||
WOPI_TOKEN_VALIDITY = 10.hours.to_i
|
||||
|
||||
TEXT_EXTRACT_FILE_TYPES = [
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
|
|
|
|||
|
|
@ -34,6 +34,13 @@ if ENV['WORKER'].present?
|
|||
RepositoryItemDateReminderJob.perform_now
|
||||
end
|
||||
|
||||
if ENV['WOPI_ENABLED'] == 'true'
|
||||
# Clean up expired WOPI tokens
|
||||
schedule_task(scheduler, '1d') do
|
||||
Token.where(ttl: ...Time.now.utc.to_i).delete_all
|
||||
end
|
||||
end
|
||||
|
||||
schedule_task(scheduler, '1d') do
|
||||
NotificationCleanupJob.perform_now
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPredefinedToLabelTemplates < ActiveRecord::Migration[7.0]
|
||||
class LabelTemplate < ActiveRecord::Base; end
|
||||
|
||||
def up
|
||||
add_column :label_templates, :predefined, :boolean, default: false, null: false
|
||||
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
LabelTemplate.where(
|
||||
name: [
|
||||
I18n.t('label_templates.default_zebra_name'),
|
||||
I18n.t('label_templates.default_zebra_name_203dpi'),
|
||||
I18n.t('label_templates.default_fluics_name')
|
||||
]
|
||||
).update_all(predefined: true)
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :label_templates, :predefined
|
||||
end
|
||||
end
|
||||
|
|
@ -24,7 +24,7 @@ describe WopiController, type: :controller do
|
|||
let(:step_asset) { create :step_asset, step: step, asset: asset }
|
||||
let(:step_asset_in_repository) { create :step_asset, step: step_in_repository, asset: asset }
|
||||
let(:result_asset) { create :result_asset, result: result, asset: asset }
|
||||
let(:token) { Token.create(token: 'token', ttl: 0, user_id: user.id) }
|
||||
let(:token) { Token.create(token: 'token', ttl: Time.now.utc.to_i + Constants::WOPI_TOKEN_VALIDITY, user_id: user.id) }
|
||||
|
||||
describe 'POST unlock' do
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -32,6 +32,6 @@ describe Comment, type: :model do
|
|||
describe 'Validations' do
|
||||
it { should validate_presence_of :message }
|
||||
it { should validate_length_of(:message).is_at_most(Constants::TEXT_MAX_LENGTH) }
|
||||
it { should validate_presence_of :user }
|
||||
it { should belong_to(:user) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue