Merge branch 'develop' into features/user-groups

This commit is contained in:
Martin Artnik 2025-06-30 15:24:22 +02:00
commit a6e5cc9d2e
16 changed files with 150 additions and 81 deletions

View file

@ -1 +1 @@
1.42.0 1.42.0.1

View file

@ -255,7 +255,6 @@ label {
.user-time { .user-time {
color: $color-silver-chalice; color: $color-silver-chalice;
white-space: nowrap;
} }
.report-element-body { .report-element-body {
@ -462,7 +461,6 @@ label {
.user-time { .user-time {
display: inline-block; display: inline-block;
white-space: nowrap;
} }
} }

View file

@ -41,7 +41,12 @@ class ResultsController < ApplicationController
end end
def list def list
if params[:with_linked_step_id].present?
step = @my_module.protocol.steps.find_by(id: params[:with_linked_step_id])
@results = @my_module.results.where(archived: false).or(@my_module.results.where(id: step.results.select(:id)))
else
@results = @my_module.results.active @results = @my_module.results.active
end
update_and_apply_user_sort_preference! update_and_apply_user_sort_preference!
end end

View file

@ -87,7 +87,8 @@ class StepResultsController < ApplicationController
my_module: my_module.id, my_module: my_module.id,
step: step.id, step: step.id,
result: result.id, result: result.id,
position: step.position + 1 step_position: { id: step.id,
value_for: 'position_plus_one' }
}) })
end end
end end

View file

@ -285,7 +285,7 @@ class StepsController < ApplicationController
render json: { render json: {
paginated: true, paginated: true,
next_page: steps.next_page, next_page: steps.next_page,
data: steps.map { |step| [step.id, step.name] } data: steps.map { |step| [step.id, step.name, { position: step.position }] }
} }
end end

View file

@ -11,7 +11,10 @@
{{ i18n.t('protocols.steps.modals.link_results.title') }} {{ i18n.t('protocols.steps.modals.link_results.title') }}
</h4> </h4>
</div> </div>
<div v-if="results.length > 0" class="modal-body"> <div v-if="loading" class="modal-body h-40 flex items-center justify-center">
<div class="sci-loader"></div>
</div>
<div v-else-if="results.length > 0" class="modal-body">
<p> <p>
{{ i18n.t('protocols.steps.modals.link_results.description') }} {{ i18n.t('protocols.steps.modals.link_results.description') }}
</p> </p>
@ -32,7 +35,7 @@
{{ i18n.t('protocols.steps.modals.link_results.empty_description') }} {{ i18n.t('protocols.steps.modals.link_results.empty_description') }}
</p> </p>
</div> </div>
<div class="modal-footer"> <div v-if="!loading" class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"> <button type="button" class="btn btn-secondary" data-dismiss="modal">
{{ i18n.t('general.cancel') }} {{ i18n.t('general.cancel') }}
</button> </button>
@ -89,12 +92,13 @@ export default {
return { return {
results: [], results: [],
initialResults: [], initialResults: [],
selectedResults: [] selectedResults: [],
loading: true
}; };
}, },
computed: { computed: {
resultsListUrl() { resultsListUrl() {
return list_my_module_results_path({ my_module_id: this.step.attributes.my_module_id }); return list_my_module_results_path({ my_module_id: this.step.attributes.my_module_id, with_linked_step_id: this.step.id });
}, },
resultsPageUrl() { resultsPageUrl() {
return my_module_results_path({ my_module_id: this.step.attributes.my_module_id }); return my_module_results_path({ my_module_id: this.step.attributes.my_module_id });
@ -132,6 +136,10 @@ export default {
axios.get(this.resultsListUrl) axios.get(this.resultsListUrl)
.then((response) => { .then((response) => {
this.results = response.data; this.results = response.data;
this.loading = false;
}).catch(() => {
HelperModule.flashAlertMsg(I18n.t('general.error'), 'danger');
this.loading = false;
}); });
} }
} }

View file

@ -83,14 +83,15 @@
:data-object-type="step.attributes.type" :data-object-type="step.attributes.type"
tabindex="0" tabindex="0"
></span> <!-- Hidden element to support legacy code --> ></span> <!-- Hidden element to support legacy code -->
<template v-if="!inRepository">
<template v-if="step.attributes.results.length == 0"> <template v-if="step.attributes.results.length == 0">
<button v-if="urls.update_url" :title="i18n.t('protocols.steps.link_results')" class="btn btn-light icon-btn" @click="this.openLinkResultsModal = true"> <button ref="linkButton" v-if="urls.update_url" :title="i18n.t('protocols.steps.link_results')" class="btn btn-light icon-btn" @click="this.openLinkResultsModal = true">
<i class="sn-icon sn-icon-results"></i> <i class="sn-icon sn-icon-results"></i>
</button> </button>
</template> </template>
<GeneralDropdown v-else ref="linkedResultsDropdown" position="right"> <GeneralDropdown v-else ref="linkedResultsDropdown" position="right">
<template v-slot:field> <template v-slot:field>
<button class="btn btn-light icon-btn" :title="i18n.t('protocols.steps.linked_results')"> <button ref="linkButton" class="btn btn-light icon-btn" :title="i18n.t('protocols.steps.linked_results')">
<i class="sn-icon sn-icon-results"></i> <i class="sn-icon sn-icon-results"></i>
<span class="absolute top-1 right-1 h-4 min-w-4 bg-sn-science-blue text-white flex items-center justify-center rounded-full text-[10px]"> <span class="absolute top-1 right-1 h-4 min-w-4 bg-sn-science-blue text-white flex items-center justify-center rounded-full text-[10px]">
{{ step.attributes.results.length }} {{ step.attributes.results.length }}
@ -117,6 +118,7 @@
</template> </template>
</template> </template>
</GeneralDropdown> </GeneralDropdown>
</template>
<a href=" #" <a href=" #"
v-if="!inRepository" v-if="!inRepository"
ref="comments" ref="comments"
@ -214,12 +216,14 @@
@close="openFormSelectModal = false" @close="openFormSelectModal = false"
@submit="createElement('form_response', null, null, $event); openFormSelectModal = false" @submit="createElement('form_response', null, null, $event); openFormSelectModal = false"
/> />
<Teleport to="body">
<LinkResultsModal <LinkResultsModal
v-if="openLinkResultsModal" v-if="openLinkResultsModal"
:step="step" :step="step"
@updateStep="updateLinkedResults" @updateStep="updateLinkedResults"
@close="openLinkResultsModal = false" @close="openLinkResultsModal = false"
/> />
</Teleport>
</div> </div>
</template> </template>
@ -399,6 +403,11 @@
$(this.$refs.elementsDropdownButton).on('shown.bs.dropdown hidden.bs.dropdown', () => { $(this.$refs.elementsDropdownButton).on('shown.bs.dropdown hidden.bs.dropdown', () => {
this.handleDropdownPosition(this.$refs.elementsDropdownButton, this.$refs.elementsDropdown) this.handleDropdownPosition(this.$refs.elementsDropdownButton, this.$refs.elementsDropdown)
}); });
window.initTooltip(this.$refs.linkButton);
},
beforeUnmount() {
window.destroyTooltip(this.$refs.linkButton);
}, },
computed: { computed: {
reorderableElements() { reorderableElements() {
@ -801,10 +810,14 @@
}); });
}, },
updateLinkedResults(results) { updateLinkedResults(results) {
window.destroyTooltip(this.$refs.linkButton);
this.$emit('step:update', { this.$emit('step:update', {
results: results, results: results,
position: this.step.attributes.position position: this.step.attributes.position
}) });
this.$nextTick(() => window.initTooltip(this.$refs.linkButton));
}, },
resultUrl(result_id, archived) { resultUrl(result_id, archived) {
return my_module_results_path({my_module_id: this.step.attributes.my_module_id, result_id: result_id, view_mode: (archived ? 'archived' : 'active') }); return my_module_results_path({my_module_id: this.step.attributes.my_module_id, result_id: result_id, view_mode: (archived ? 'archived' : 'active') });

View file

@ -11,7 +11,10 @@
{{ i18n.t('my_modules.results.modals.link_steps.title') }} {{ i18n.t('my_modules.results.modals.link_steps.title') }}
</h4> </h4>
</div> </div>
<div v-if="steps.length > 0" class="modal-body"> <div v-if="loading" class="modal-body h-40 flex items-center justify-center">
<div class="sci-loader"></div>
</div>
<div v-else-if="steps.length > 0" class="modal-body">
<p> <p>
{{ i18n.t('my_modules.results.modals.link_steps.description') }} {{ i18n.t('my_modules.results.modals.link_steps.description') }}
</p> </p>
@ -22,6 +25,7 @@
:value="selectedSteps" :value="selectedSteps"
:searchable="true" :searchable="true"
@change="changeSteps" @change="changeSteps"
:option-renderer="stepRenderer"
:multiple="true" :multiple="true"
:withCheckboxes="true" :withCheckboxes="true"
:placeholder="i18n.t('my_modules.results.modals.link_steps.placeholder')" /> :placeholder="i18n.t('my_modules.results.modals.link_steps.placeholder')" />
@ -32,7 +36,7 @@
{{ i18n.t('my_modules.results.modals.link_steps.empty_description') }} {{ i18n.t('my_modules.results.modals.link_steps.empty_description') }}
</p> </p>
</div> </div>
<div class="modal-footer"> <div v-if="!loading" class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"> <button type="button" class="btn btn-secondary" data-dismiss="modal">
{{ i18n.t('general.cancel') }} {{ i18n.t('general.cancel') }}
</button> </button>
@ -93,7 +97,8 @@ export default {
return { return {
steps: [], steps: [],
selectedSteps: [], selectedSteps: [],
initialSteps: [] initialSteps: [],
loading: true
}; };
}, },
computed: { computed: {
@ -135,8 +140,15 @@ export default {
loadSteps() { loadSteps() {
axios.get(this.stepsListUrl) axios.get(this.stepsListUrl)
.then((response) => { .then((response) => {
this.loading = false;
this.steps = response.data; this.steps = response.data;
}).catch(() => {
HelperModule.flashAlertMsg(I18n.t('general.error'), 'danger');
this.loading = false;
}); });
},
stepRenderer(option) {
return `${option[2].position + 1}. ${option[1]}`;
} }
} }
}; };

View file

@ -66,13 +66,13 @@
tabindex="0" tabindex="0"
></span> <!-- Hidden element to support legacy code --> ></span> <!-- Hidden element to support legacy code -->
<tempplate v-if="result.attributes.steps.length == 0"> <tempplate v-if="result.attributes.steps.length == 0">
<button v-if="urls.update_url" :title="i18n.t('my_modules.results.link_steps')" class="btn btn-light icon-btn" @click="this.openLinkStepsModal = true"> <button v-if="urls.update_url" ref="linkButton" :title="i18n.t('my_modules.results.link_steps')" class="btn btn-light icon-btn" @click="this.openLinkStepsModal = true">
{{ i18n.t('my_modules.results.link_to_step') }} {{ i18n.t('my_modules.results.link_to_step') }}
</button> </button>
</tempplate> </tempplate>
<GeneralDropdown v-else ref="linkedStepsDropdown" position="right"> <GeneralDropdown v-else ref="linkedStepsDropdown" position="right">
<template v-slot:field> <template v-slot:field>
<button class="btn btn-light icon-btn" :title="i18n.t('my_modules.results.linked_steps')"> <button ref="linkButton" class="btn btn-light icon-btn" :title="i18n.t('my_modules.results.linked_steps')">
<i class="sn-icon sn-icon-steps"></i> <i class="sn-icon sn-icon-steps"></i>
<span class="absolute top-1 -right-1 h-4 min-w-4 bg-sn-science-blue text-white flex items-center justify-center rounded-full text-[10px]"> <span class="absolute top-1 -right-1 h-4 min-w-4 bg-sn-science-blue text-white flex items-center justify-center rounded-full text-[10px]">
{{ result.attributes.steps.length }} {{ result.attributes.steps.length }}
@ -181,6 +181,7 @@
@cancel="closeCustomWellPlateModal" @cancel="closeCustomWellPlateModal"
@create:table="(...args) => this.createElement('table', ...args)" @create:table="(...args) => this.createElement('table', ...args)"
/> />
<Teleport to="body">
<LinkStepsModal <LinkStepsModal
v-if="openLinkStepsModal" v-if="openLinkStepsModal"
:result="result" :result="result"
@ -188,6 +189,7 @@
@updateResult="updateLinkedSteps" @updateResult="updateLinkedSteps"
@close="openLinkStepsModal = false" @close="openLinkStepsModal = false"
/> />
</Teleport>
</div> </div>
</div> </div>
</div> </div>
@ -302,6 +304,11 @@ export default {
} }
this.$emit('result:collapsed'); this.$emit('result:collapsed');
}); });
window.initTooltip(this.$refs.linkButton);
},
beforeUnmount() {
window.destroyTooltip(this.$refs.linkButton);
}, },
computed: { computed: {
reorderableElements() { reorderableElements() {
@ -627,9 +634,13 @@ export default {
}); });
}, },
updateLinkedSteps(steps) { updateLinkedSteps(steps) {
window.destroyTooltip(this.$refs.linkButton);
this.$emit('result:update', this.result.id,{ this.$emit('result:update', this.result.id,{
steps: steps steps: steps
}) });
this.$nextTick(() => window.initTooltip(this.$refs.linkButton));
}, },
protocolUrl(step_id) { protocolUrl(step_id) {
return protocols_my_module_path({ id: this.result.attributes.my_module_id }, { step_id: step_id }) return protocols_my_module_path({ id: this.result.attributes.my_module_id }, { step_id: step_id })

View file

@ -270,7 +270,8 @@ export default {
maxWidth: 40, maxWidth: 40,
resizable: true, resizable: true,
pinned: 'left', pinned: 'left',
lockPosition: 'left' lockPosition: 'left',
sortable: false
}); });
} }
@ -453,6 +454,9 @@ export default {
currentViewRender: this.currentViewRender, currentViewRender: this.currentViewRender,
perPage: this.perPage perPage: this.perPage
}; };
columnsState.find((column) => column.colId === 'checkbox').pinned = 'left';
const settings = { const settings = {
key: this.stateKey, key: this.stateKey,
data: tableState data: tableState
@ -532,6 +536,11 @@ export default {
this.restoreSelection(); this.restoreSelection();
this.handleScroll(); this.handleScroll();
})
.catch(() => {
this.dataLoading = false;
this.$emit('tableReloaded', [], { filtered: this.searchValue.length > 0 });
window.HelperModule.flashAlertMsg(this.i18n.t('general.error'), 'danger');
}); });
}, },
handleInfiniteScroll(response) { handleInfiniteScroll(response) {

View file

@ -260,7 +260,7 @@ class Protocol < ApplicationRecord
def self.viewable_by_user_my_module_protocols(user, teams) def self.viewable_by_user_my_module_protocols(user, teams)
distinct.joins(:my_module) distinct.joins(:my_module)
.where(my_modules: MyModule.viewable_by_user(user, teams)) .where(my_modules: { id: MyModule.viewable_by_user(user, teams).select(:id) })
end end
def self.filter_by_teams(teams = []) def self.filter_by_teams(teams = [])

View file

@ -39,6 +39,7 @@ class Result < ApplicationRecord
accepts_nested_attributes_for :tables accepts_nested_attributes_for :tables
before_save :ensure_default_name before_save :ensure_default_name
after_discard :delete_step_results
after_discard do after_discard do
CleanupUserSettingsJob.perform_later('result_states', id) CleanupUserSettingsJob.perform_later('result_states', id)
end end
@ -51,8 +52,12 @@ class Result < ApplicationRecord
teams = options[:teams] || current_team || user.teams.select(:id) teams = options[:teams] || current_team || user.teams.select(:id)
new_query = joins(:my_module) new_query = joins(:my_module)
.where(my_modules: MyModule.with_granted_permissions(user, MyModulePermissions::READ) .where(
.where(user_assignments: { team: teams })) my_modules: {
id: MyModule.with_granted_permissions(user, MyModulePermissions::READ)
.where(user_assignments: { team: teams }).select(:id)
}
)
unless include_archived unless include_archived
new_query = new_query.joins(my_module: { experiment: :project }) new_query = new_query.joins(my_module: { experiment: :project })
@ -187,4 +192,8 @@ class Result < ApplicationRecord
def ensure_default_name def ensure_default_name
self.name = name.presence || I18n.t('my_modules.results.default_name') self.name = name.presence || I18n.t('my_modules.results.default_name')
end end
def delete_step_results
step_results.destroy_all
end
end end

View file

@ -81,6 +81,9 @@ module Scinote
config.x.export_all_limit_24h = (ENV['EXPORT_ALL_LIMIT_24_HOURS'] || 3).to_i config.x.export_all_limit_24h = (ENV['EXPORT_ALL_LIMIT_24_HOURS'] || 3).to_i
# Fallback to old variant behaviour (pre 7.2)
config.active_storage.track_variants = false
# SciNote Core Application version # SciNote Core Application version
VERSION = File.read(Rails.root.join('VERSION')).strip.freeze VERSION = File.read(Rails.root.join('VERSION')).strip.freeze

View file

@ -430,7 +430,7 @@ en:
attachments: attachments:
menu: menu:
office_file: "New Office file" office_file: "New Microsoft 365 file"
chemical_drawing: "New chemical drawing" chemical_drawing: "New chemical drawing"
file_from_pc: "File from your PC" file_from_pc: "File from your PC"
modified_label: "Modified:" modified_label: "Modified:"
@ -2215,9 +2215,9 @@ en:
success_flash: "File result successfully deleted." success_flash: "File result successfully deleted."
wopi_open_file: "Open in %{app}" wopi_open_file: "Open in %{app}"
wopi_edit_file: "Open in %{app}" wopi_edit_file: "Open in %{app}"
wopi_word: "Microsoft Word for the Web" wopi_word: "Microsoft Word for the web"
wopi_excel: "Microsoft Excel for the Web" wopi_excel: "Microsoft Excel for the web"
wopi_powerpoint: "Microsoft PowerPoint for the Web" wopi_powerpoint: "Microsoft PowerPoint for the web"
error_flash: 'Something went wrong! Please try again later.' error_flash: 'Something went wrong! Please try again later.'
result_tables: result_tables:
@ -4440,9 +4440,9 @@ en:
wopi_supported_table_formats_title: 'Only .xlsx, .xlsm, .xlsb, .ods file formats are supported for editing in Excel for the web.' wopi_supported_table_formats_title: 'Only .xlsx, .xlsm, .xlsb, .ods file formats are supported for editing in Excel for the web.'
wopi_supported_presentation_formats_title: 'Only .pptx, ppsx, .odp file formats are supported for editing in PowerPoint for the web.' wopi_supported_presentation_formats_title: 'Only .pptx, ppsx, .odp file formats are supported for editing in PowerPoint for the web.'
create_wopi_file: create_wopi_file:
button_text: 'New Microsoft Office file' button_text: 'New Microsoft 365 file'
li_text: "Office file" li_text: "Microsoft 365 file"
modal_title: 'Create new Microsoft Office document' modal_title: 'Create new Microsoft 365 document'
text_field_label: 'Document name' text_field_label: 'Document name'
type_select_label: 'Document type' type_select_label: 'Document type'
text_field_placeholder: 'Enter document name...' text_field_placeholder: 'Enter document name...'

View file

@ -122,12 +122,12 @@ en:
move_experiment_html: "%{user} moved experiment %{experiment} from project %{project_original} to project %{project_new}." move_experiment_html: "%{user} moved experiment %{experiment} from project %{project_original} to project %{project_new}."
clone_experiment_html: "%{user} duplicated experiment %{experiment_new} from experiment %{experiment_original} as template." clone_experiment_html: "%{user} duplicated experiment %{experiment_new} from experiment %{experiment_original} as template."
archive_experiment_html: "%{user} archived experiment %{experiment}." archive_experiment_html: "%{user} archived experiment %{experiment}."
edit_wopi_file_on_result_html: "%{user} edited Office online file %{asset_name} on result %{result}: %{action}." edit_wopi_file_on_result_html: "%{user} edited Microsoft 365 file %{asset_name} on result %{result}: %{action}."
export_protocol_from_task_html: "%{user} exported protocol from task %{my_module}" export_protocol_from_task_html: "%{user} exported protocol from task %{my_module}"
copy_protocol_in_repository_html: "%{user} duplicated protocol %{protocol_new} from protocol %{protocol_original} as a template" copy_protocol_in_repository_html: "%{user} duplicated protocol %{protocol_new} from protocol %{protocol_original} as a template"
user_leave_team_html: "%{user} left team %{team}" user_leave_team_html: "%{user} left team %{team}"
edit_wopi_file_on_step_html: "%{user} edited Office online file %{asset_name} on protocol's step %{step_position} %{step} on task %{my_module}: %{action}." edit_wopi_file_on_step_html: "%{user} edited Microsoft 365 file %{asset_name} on protocol's step %{step_position} %{step} on task %{my_module}: %{action}."
edit_wopi_file_on_step_in_repository_html: "%{user} edited Office online file %{asset_name} on protocol %{protocol}'s step %{step_position} %{step} in Protocol repository: %{action}." edit_wopi_file_on_step_in_repository_html: "%{user} edited Microsoft 365 file %{asset_name} on protocol %{protocol}'s step %{step_position} %{step} in Protocol repository: %{action}."
restore_experiment_html: "%{user} restored experiment %{experiment}." restore_experiment_html: "%{user} restored experiment %{experiment}."
rename_task_html: "%{user} renamed task %{my_module}." rename_task_html: "%{user} renamed task %{my_module}."
move_task_html: "%{user} moved task %{my_module} from experiment %{experiment_original} to experiment %{experiment_new}." move_task_html: "%{user} moved task %{my_module} from experiment %{experiment_original} to experiment %{experiment_new}."
@ -313,7 +313,7 @@ en:
edit_task_result_file_locally_html: "%{user} locally edited file %{file} on result %{result}" edit_task_result_file_locally_html: "%{user} locally edited file %{file} on result %{result}"
export_inventories_html: "%{user} exported inventory %{inventories}" export_inventories_html: "%{user} exported inventory %{inventories}"
edit_image_on_inventory_item_html: "%{user} edited image %{asset_name} on inventory item %{repository_row} in inventory %{repository}: %{action}." edit_image_on_inventory_item_html: "%{user} edited image %{asset_name} on inventory item %{repository_row} in inventory %{repository}: %{action}."
edit_wopi_file_on_inventory_item_html: "%{user} edited Office online file %{asset_name} on inventory item %{repository_row} in inventory %{repository}: %{action}." edit_wopi_file_on_inventory_item_html: "%{user} edited Microsoft 365 file %{asset_name} on inventory item %{repository_row} in inventory %{repository}: %{action}."
export_inventory_stock_consumption_html: "%{user} exported stock consumption for inventory item(s) %{inventory_items} in inventory %{repository}." export_inventory_stock_consumption_html: "%{user} exported stock consumption for inventory item(s) %{inventory_items} in inventory %{repository}."
task_step_file_duplicated_html: "%{user} duplicated file <strong>%{file}</strong> on protocol's step <strong>%{step}</strong> on task <strong>%{my_module}</strong>." task_step_file_duplicated_html: "%{user} duplicated file <strong>%{file}</strong> on protocol's step <strong>%{step}</strong> on task <strong>%{my_module}</strong>."
result_file_duplicated_html: "%{user} duplicated file <strong>%{file}</strong> on result <strong>%{result}</strong> on task <strong>%{my_module}</strong>." result_file_duplicated_html: "%{user} duplicated file <strong>%{file}</strong> on result <strong>%{result}</strong> on task <strong>%{my_module}</strong>."
@ -410,6 +410,8 @@ en:
my_module_access_changed_user_group_html: "%{user} changed %{user_group}'s role on task %{my_module} to %{role}." my_module_access_changed_user_group_html: "%{user} changed %{user_group}'s role on task %{my_module} to %{role}."
step_and_result_linked_html: "%{user} linked result <strong>%{result}</strong> and step <strong>%{position}</strong> <strong>%{step}</strong> on task <strong>%{my_module}</strong>." step_and_result_linked_html: "%{user} linked result <strong>%{result}</strong> and step <strong>%{position}</strong> <strong>%{step}</strong> on task <strong>%{my_module}</strong>."
step_and_result_unlinked_html: "%{user} unlinked result <strong>%{result}</strong> and step <strong>%{position}</strong> <strong>%{step}</strong> on task <strong>%{my_module}</strong>." step_and_result_unlinked_html: "%{user} unlinked result <strong>%{result}</strong> and step <strong>%{position}</strong> <strong>%{step}</strong> on task <strong>%{my_module}</strong>."
step_and_result_linked_html: "%{user} linked result <strong>%{result}</strong> and step <strong>%{step_position}</strong> <strong>%{step}</strong> on task <strong>%{my_module}</strong>."
step_and_result_unlinked_html: "%{user} unlinked result <strong>%{result}</strong> and step <strong>%{step_position}</strong> <strong>%{step}</strong> on task <strong>%{my_module}</strong>."
activity_name: activity_name:
create_project: "Project created" create_project: "Project created"
edit_project: "Project edited" edit_project: "Project edited"
@ -486,9 +488,9 @@ en:
move_experiment: "Experiment moved" move_experiment: "Experiment moved"
clone_experiment: "Experiment duplicated" clone_experiment: "Experiment duplicated"
archive_experiment: "Experiment archived" archive_experiment: "Experiment archived"
edit_wopi_file_on_result: "Office online file on result edited" edit_wopi_file_on_result: "Microsoft 365 file on result edited"
edit_wopi_file_on_step: "Office online file on task step edited" edit_wopi_file_on_step: "Microsoft 365 file on task step edited"
edit_wopi_file_on_step_in_repository: "Office online file on step edited" edit_wopi_file_on_step_in_repository: "Microsoft 365 file on step edited"
restore_experiment: "Experiment restored" restore_experiment: "Experiment restored"
rename_task: "Task renamed" rename_task: "Task renamed"
move_task: "Task moved" move_task: "Task moved"

View file

@ -1,8 +1,6 @@
include UsersGenerator include UsersGenerator
if ActiveRecord::Base.connection.migration_context.needs_migration? ActiveRecord::Migration.check_all_pending!
raise "There are pending migrations. Run 'rails db:migrate' first."
end
MyModuleStatusFlow.ensure_default MyModuleStatusFlow.ensure_default