mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-10 13:59:56 +08:00
Merge pull request #8258 from andrej-scinote/aj_SCI_11558
Create new item from task [SCI-11558]
This commit is contained in:
commit
b25ebf4324
10 changed files with 202 additions and 13 deletions
|
@ -82,9 +82,27 @@ class RepositoryRowsController < ApplicationController
|
||||||
record_annotation_notification(repository_row, repository_cell)
|
record_annotation_notification(repository_row, repository_cell)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if update_params[:my_module_id].present?
|
||||||
|
my_module = MyModule.viewable_by_user(current_user, current_team).find_by(id: update_params[:my_module_id])
|
||||||
|
|
||||||
|
return render_403 unless my_module.present? && can_read_my_module?(my_module)
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
service_assign = RepositoryRows::MyModuleAssignUnassignService.call(
|
||||||
|
my_module: my_module,
|
||||||
|
repository: @repository,
|
||||||
|
user: current_user,
|
||||||
|
params: { rows_to_assign: [repository_row.id] }
|
||||||
|
)
|
||||||
|
|
||||||
|
render json: service_assign.errors, status: :bad_request unless service_assign.succeed?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
render json: { id: service.repository_row.id, flash: t('repositories.create.success_flash',
|
render json: { id: service.repository_row.id, flash: t('repositories.create.success_flash',
|
||||||
record: escape_input(repository_row.name),
|
record: escape_input(repository_row.name),
|
||||||
repository: escape_input(@repository.name)) },
|
repository: escape_input(@repository.name)),
|
||||||
|
repository_row_url: repository_repository_row_path(@repository, repository_row) },
|
||||||
status: :ok
|
status: :ok
|
||||||
else
|
else
|
||||||
render json: service.errors, status: :bad_request
|
render json: service.errors, status: :bad_request
|
||||||
|
@ -480,7 +498,7 @@ class RepositoryRowsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_params
|
def update_params
|
||||||
params.permit(repository_row: :name, repository_cells: {}).to_h
|
params.permit(:my_module_id, :is_output, repository_row: :name, repository_cells: {}).to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_activity(type_of, repository_row, message_items = {})
|
def log_activity(type_of, repository_row, message_items = {})
|
||||||
|
|
|
@ -108,7 +108,7 @@ module RepositoryDatatableHelper
|
||||||
repository_rows.map do |record|
|
repository_rows.map do |record|
|
||||||
row = {
|
row = {
|
||||||
DT_RowId: record.id,
|
DT_RowId: record.id,
|
||||||
DT_RowAttr: { 'data-state': row_style(record) },
|
DT_RowAttr: { 'data-state': simple_row_style(record, my_module) },
|
||||||
'0': escape_input(record.name),
|
'0': escape_input(record.name),
|
||||||
recordInfoUrl: Rails.application.routes.url_helpers.repository_repository_row_path(record.repository, record),
|
recordInfoUrl: Rails.application.routes.url_helpers.repository_repository_row_path(record.repository, record),
|
||||||
rowRemindersUrl:
|
rowRemindersUrl:
|
||||||
|
@ -305,6 +305,14 @@ module RepositoryDatatableHelper
|
||||||
''
|
''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def simple_row_style(row, my_module)
|
||||||
|
style = []
|
||||||
|
style << I18n.t('general.archived') if row.archived
|
||||||
|
style << I18n.t('general.output') if row.output? && row.my_module.id == my_module&.id
|
||||||
|
|
||||||
|
style
|
||||||
|
end
|
||||||
|
|
||||||
def stock_consumption_permitted?(repository, my_module)
|
def stock_consumption_permitted?(repository, my_module)
|
||||||
return false unless repository.is_a?(Repository) && current_user
|
return false unless repository.is_a?(Repository) && current_user
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
{{ i18n.t('my_modules.assigned_items.title') }}
|
{{ i18n.t('my_modules.assigned_items.title') }}
|
||||||
<span class="text-sn-grey-500 font-normal text-base">[{{ totalRows }}]</span>
|
<span class="text-sn-grey-500 font-normal text-base">[{{ totalRows }}]</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="ml-auto">
|
<div class="flex gap-6 ml-auto">
|
||||||
|
<button class="btn btn-secondary" @click="openCreateItemModal=true">
|
||||||
|
{{ i18n.t('my_modules.assigned_items.create_item') }}
|
||||||
|
</button>
|
||||||
<!-- Next block just for legacy support, JQuery not good works with Teleport -->
|
<!-- Next block just for legacy support, JQuery not good works with Teleport -->
|
||||||
<div class="hidden repository-assign"
|
<div class="hidden repository-assign"
|
||||||
v-for="repository in availableRepositories"
|
v-for="repository in availableRepositories"
|
||||||
|
@ -20,8 +23,9 @@
|
||||||
<!-- End of block -->
|
<!-- End of block -->
|
||||||
<GeneralDropdown position="right" @open="loadAvailableRepositories">
|
<GeneralDropdown position="right" @open="loadAvailableRepositories">
|
||||||
<template v-slot:field>
|
<template v-slot:field>
|
||||||
<button class="btn btn-light">
|
<button class="btn btn-secondary">
|
||||||
{{ i18n.t('my_modules.assigned_items.assign_from') }}
|
{{ i18n.t('my_modules.assigned_items.assign_from') }}
|
||||||
|
<span class="sn-icon sn-icon-down"></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:flyout>
|
<template v-slot:flyout>
|
||||||
|
@ -52,6 +56,15 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Teleport to="body">
|
||||||
|
<CreateItemModal
|
||||||
|
v-if="openCreateItemModal"
|
||||||
|
:repositoriesUrl="repositoriesUrl"
|
||||||
|
:myModuleId="myModuleId"
|
||||||
|
@tableReloaded="newCreatedRow"
|
||||||
|
@close="openCreateItemModal = false"/>
|
||||||
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -59,16 +72,20 @@
|
||||||
import axios from '../../packs/custom_axios.js';
|
import axios from '../../packs/custom_axios.js';
|
||||||
import GeneralDropdown from '../shared/general_dropdown.vue';
|
import GeneralDropdown from '../shared/general_dropdown.vue';
|
||||||
import AssignedRepository from './assigned_items/repository.vue';
|
import AssignedRepository from './assigned_items/repository.vue';
|
||||||
|
import CreateItemModal from './assigned_items/modals/new_item.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AssignedItems',
|
name: 'AssignedItems',
|
||||||
props: {
|
props: {
|
||||||
avaialableRepositoriesUrl: String,
|
avaialableRepositoriesUrl: String,
|
||||||
assignedRepositoriesUrl: String
|
assignedRepositoriesUrl: String,
|
||||||
|
repositoriesUrl: String,
|
||||||
|
myModuleId: String
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
GeneralDropdown,
|
GeneralDropdown,
|
||||||
AssignedRepository
|
AssignedRepository,
|
||||||
|
CreateItemModal
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadAssingedRepositories();
|
this.loadAssingedRepositories();
|
||||||
|
@ -83,7 +100,8 @@ export default {
|
||||||
availableRepositories: [],
|
availableRepositories: [],
|
||||||
assignedRepositories: [],
|
assignedRepositories: [],
|
||||||
loadingAvailableRepositories: false,
|
loadingAvailableRepositories: false,
|
||||||
sectionOpened: false
|
sectionOpened: false,
|
||||||
|
openCreateItemModal: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -111,6 +129,10 @@ export default {
|
||||||
this.assignedRepositories = response.data.data;
|
this.assignedRepositories = response.data.data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
newCreatedRow(repositoryRowSidebarUrl) {
|
||||||
|
this.loadAssingedRepositories();
|
||||||
|
window.repositoryItemSidebarComponent.toggleShowHideSidebar(repositoryRowSidebarUrl, this.myModuleId, null);
|
||||||
|
},
|
||||||
openAssignModal(repositoryId) {
|
openAssignModal(repositoryId) {
|
||||||
const [repository] = this.$refs[`repository_${repositoryId}`];
|
const [repository] = this.$refs[`repository_${repositoryId}`];
|
||||||
repository.click();
|
repository.click();
|
||||||
|
|
112
app/javascript/vue/my_module/assigned_items/modals/new_item.vue
Normal file
112
app/javascript/vue/my_module/assigned_items/modals/new_item.vue
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<template>
|
||||||
|
<div ref="modal" class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<i class="sn-icon sn-icon-close"></i>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title truncate !block" >
|
||||||
|
{{ i18n.t('my_modules.assigned_items.create_modal.modal_title') }}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="mb-6">{{ i18n.t('my_modules.assigned_items.create_modal.description') }}</p>
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="sci-label">
|
||||||
|
{{ i18n.t('my_modules.assigned_items.create_modal.inventory_label') }}
|
||||||
|
</label>
|
||||||
|
<SelectDropdown :optionsUrl="repositoriesUrl" @change="changeRepository" :searchable="true" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="sci-label">
|
||||||
|
{{ i18n.t('my_modules.assigned_items.create_modal.inventory_item_name') }}
|
||||||
|
</label>
|
||||||
|
<div class="sci-input-container-v2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="inventoryItemName"
|
||||||
|
:placeholder="i18n.t('my_modules.assigned_items.create_modal.inventory_item_name')"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2.5">
|
||||||
|
<span class="sci-checkbox-container">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="sci-checkbox"
|
||||||
|
v-model="outputMarked" />
|
||||||
|
<span class="sci-checkbox-label"></span>
|
||||||
|
</span>
|
||||||
|
{{ i18n.t('my_modules.assigned_items.create_modal.checkbox_label') }}
|
||||||
|
</div>
|
||||||
|
</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="submitting || !validObject" type="submit">
|
||||||
|
{{ i18n.t('my_modules.assigned_items.create_modal.create_item') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import axios from '../../../../packs/custom_axios.js';
|
||||||
|
import SelectDropdown from '../../../shared/select_dropdown.vue';
|
||||||
|
import modalMixin from '../../../shared/modal_mixin.js';
|
||||||
|
import {
|
||||||
|
repository_repository_rows_path
|
||||||
|
} from '../../../../routes.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateItemModal',
|
||||||
|
props: {
|
||||||
|
repositoriesUrl: String,
|
||||||
|
myModuleId: String
|
||||||
|
},
|
||||||
|
mixins: [modalMixin],
|
||||||
|
components: {
|
||||||
|
SelectDropdown
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedRepository: null,
|
||||||
|
inventoryItemName: '',
|
||||||
|
outputMarked: false,
|
||||||
|
submitting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validObject() {
|
||||||
|
return this.selectedRepository && this.inventoryItemName;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeRepository(repository) {
|
||||||
|
this.selectedRepository = repository;
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
this.submitting = true;
|
||||||
|
axios.post(repository_repository_rows_path(this.selectedRepository), {
|
||||||
|
repository_row: { name: this.inventoryItemName },
|
||||||
|
my_module_id: this.myModuleId,
|
||||||
|
is_output: this.outputMarked
|
||||||
|
}).then((data) => {
|
||||||
|
this.$emit('tableReloaded', data.data.repository_row_url);
|
||||||
|
this.submitting = false;
|
||||||
|
this.close();
|
||||||
|
}).catch(() => {
|
||||||
|
this.submitting = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -10,14 +10,16 @@
|
||||||
<ul v-html="reminders" class="list-none pl-0"></ul>
|
<ul v-html="reminders" class="list-none pl-0"></ul>
|
||||||
</template>
|
</template>
|
||||||
</GeneralDropdown>
|
</GeneralDropdown>
|
||||||
<a class="hover:no-underline flex items-center gap-1 record-info-link"
|
<a class="hover:no-underline record-info-link truncate block"
|
||||||
:title="params.data[0]"
|
:title="params.data[0]"
|
||||||
:href="params.data.recordInfoUrl"
|
:href="params.data.recordInfoUrl"
|
||||||
>
|
>
|
||||||
<span class="truncate">
|
{{ params.data[0] }}
|
||||||
{{ params.data[0] }}
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<span v-for="state in params.data.DT_RowAttr['data-state']" class="text-sn-grey bg-sn-light-grey text-xs px-1.5 py-1 ">
|
||||||
|
{{ state }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ class RepositoryRow < ApplicationRecord
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
inverse_of: :restored_repository_rows,
|
inverse_of: :restored_repository_rows,
|
||||||
optional: true
|
optional: true
|
||||||
|
belongs_to :my_module, optional: true
|
||||||
has_many :repository_cells, -> { order(:id) }, inverse_of: :repository_row, dependent: :destroy
|
has_many :repository_cells, -> { order(:id) }, inverse_of: :repository_row, dependent: :destroy
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -238,4 +239,8 @@ class RepositoryRow < ApplicationRecord
|
||||||
def archived_branch?
|
def archived_branch?
|
||||||
archived?
|
archived?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def output?
|
||||||
|
my_module_id.present?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,12 +6,13 @@ module RepositoryRows
|
||||||
|
|
||||||
attr_reader :repository, :params, :errors, :repository_row
|
attr_reader :repository, :params, :errors, :repository_row
|
||||||
|
|
||||||
def initialize(repository:, user:, params:)
|
def initialize(repository:, user:, params:, my_module: nil)
|
||||||
@repository = repository
|
@repository = repository
|
||||||
@user = user
|
@user = user
|
||||||
@params = params
|
@params = params
|
||||||
@errors = {}
|
@errors = {}
|
||||||
@repository_row = nil
|
@repository_row = nil
|
||||||
|
@my_module = my_module
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
|
@ -20,7 +21,9 @@ module RepositoryRows
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
@repository_row = @repository.repository_rows.new(params[:repository_row])
|
@repository_row = @repository.repository_rows.new(params[:repository_row])
|
||||||
@repository_row.last_modified_by = @user
|
@repository_row.last_modified_by = @user
|
||||||
|
@repository_row.my_module = @my_module
|
||||||
@repository_row.created_by = @user
|
@repository_row.created_by = @user
|
||||||
|
@repository_row.my_module_id = params[:my_module_id] if params[:my_module_id] && params[:is_output]
|
||||||
@repository_row.save!
|
@repository_row.save!
|
||||||
|
|
||||||
params[:repository_cells]&.each do |column_id, value|
|
params[:repository_cells]&.each do |column_id, value|
|
||||||
|
|
|
@ -84,6 +84,8 @@
|
||||||
ref="assignedItems"
|
ref="assignedItems"
|
||||||
avaialable-repositories-url="<%= my_module_repositories_dropdown_list_path(@my_module) %>"
|
avaialable-repositories-url="<%= my_module_repositories_dropdown_list_path(@my_module) %>"
|
||||||
assigned-repositories-url="<%= my_module_repositories_list_path(@my_module) %>"
|
assigned-repositories-url="<%= my_module_repositories_list_path(@my_module) %>"
|
||||||
|
repositories-url="<%= list_team_repositories_path(current_team) %>"
|
||||||
|
my-module-id="<%= @my_module.id%>"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Assigned items -->
|
<!-- Assigned items -->
|
||||||
|
|
|
@ -1326,6 +1326,15 @@ en:
|
||||||
assigned_items:
|
assigned_items:
|
||||||
title: "Assigned items"
|
title: "Assigned items"
|
||||||
assign_from: "Assign from"
|
assign_from: "Assign from"
|
||||||
|
create_item: "Create item"
|
||||||
|
create_modal:
|
||||||
|
modal_title: 'Create a new item'
|
||||||
|
description: 'Choose an inventory and name the new item'
|
||||||
|
inventory_label: 'Inventory'
|
||||||
|
inventory_placeholder: 'Select inventory'
|
||||||
|
inventory_item_name: 'Item name'
|
||||||
|
create_item: 'Create item'
|
||||||
|
checkbox_label: 'Mark as output'
|
||||||
direct_assign:
|
direct_assign:
|
||||||
success: "Successfully assigned an item to the task."
|
success: "Successfully assigned an item to the task."
|
||||||
protocol:
|
protocol:
|
||||||
|
@ -4435,6 +4444,7 @@ en:
|
||||||
archive: "Archive"
|
archive: "Archive"
|
||||||
assign: "Assign"
|
assign: "Assign"
|
||||||
options_selected: 'options selected'
|
options_selected: 'options selected'
|
||||||
|
output: 'Output'
|
||||||
# In order to use the strings 'yes' and 'no' as keys, you need to wrap them with quotes
|
# In order to use the strings 'yes' and 'no' as keys, you need to wrap them with quotes
|
||||||
'yes': "Yes"
|
'yes': "Yes"
|
||||||
'no': "No"
|
'no': "No"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddMyModuleToRepositoryRow < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_reference :repository_rows, :my_module, null: true, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue