mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 14:45:56 +08:00
Merge pull request #7719 from aignatov-bio/ai-sci-10857-add-locations-page
Add storage locations table [SCI-10857][SCI-10858]
This commit is contained in:
commit
33626e0ddd
|
@ -4,10 +4,17 @@ class StorageLocationsController < ApplicationController
|
|||
before_action :load_storage_location, only: %i(update destroy)
|
||||
before_action :check_read_permissions, only: :index
|
||||
before_action :check_manage_permissions, except: :index
|
||||
before_action :set_breadcrumbs_items, only: :index
|
||||
|
||||
def index
|
||||
storage_locations = Lists::StorageLocationsService.new(current_team, storage_location_params).call
|
||||
render json: storage_locations, each_serializer: Lists::StorageLocationSerializer
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
storage_locations = Lists::StorageLocationsService.new(current_team, params).call
|
||||
render json: storage_locations, each_serializer: Lists::StorageLocationSerializer,
|
||||
user: current_user, meta: pagination_dict(storage_locations)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -43,6 +50,12 @@ class StorageLocationsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def actions_toolbar
|
||||
render json: {
|
||||
actions: [] # TODO: Add actions
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage_location_params
|
||||
|
@ -62,4 +75,37 @@ class StorageLocationsController < ApplicationController
|
|||
def check_manage_permissions
|
||||
render_403 unless true
|
||||
end
|
||||
|
||||
def set_breadcrumbs_items
|
||||
@breadcrumbs_items = []
|
||||
|
||||
@breadcrumbs_items.push({
|
||||
label: t('breadcrumbs.inventories')
|
||||
})
|
||||
|
||||
@breadcrumbs_items.push({
|
||||
label: t('breadcrumbs.locations'),
|
||||
url: storage_locations_path
|
||||
})
|
||||
|
||||
storage_locations = []
|
||||
if params[:parent_id]
|
||||
location = StorageLocation.where(team: current_team).find_by(id: params[:parent_id])
|
||||
if location
|
||||
storage_locations.unshift(breadcrumbs_item(location))
|
||||
while location.parent
|
||||
location = location.parent
|
||||
storage_locations.unshift(breadcrumbs_item(location))
|
||||
end
|
||||
end
|
||||
end
|
||||
@breadcrumbs_items += storage_locations
|
||||
end
|
||||
|
||||
def breadcrumbs_item(location)
|
||||
{
|
||||
label: location.name,
|
||||
url: storage_locations_path(parent_id: location.id)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,8 +19,16 @@ module LeftMenuBarHelper
|
|||
url: repositories_path,
|
||||
name: t('left_menu_bar.repositories'),
|
||||
icon: 'sn-icon-inventory',
|
||||
active: repositories_are_selected?,
|
||||
submenu: []
|
||||
active: repositories_are_selected? || storage_locations_are_selected?,
|
||||
submenu: [{
|
||||
url: repositories_path,
|
||||
name: t('left_menu_bar.items'),
|
||||
active: repositories_are_selected?
|
||||
}, {
|
||||
url: storage_locations_path,
|
||||
name: t('left_menu_bar.locations'),
|
||||
active: storage_locations_are_selected?
|
||||
}]
|
||||
}, {
|
||||
url: "#",
|
||||
name: t('left_menu_bar.templates'),
|
||||
|
@ -63,6 +71,10 @@ module LeftMenuBarHelper
|
|||
controller_name == 'repositories'
|
||||
end
|
||||
|
||||
def storage_locations_are_selected?
|
||||
controller_name == 'storage_locations'
|
||||
end
|
||||
|
||||
def protocols_are_selected?
|
||||
controller_name == 'protocols'
|
||||
end
|
||||
|
|
10
app/javascript/packs/vue/storage_locations_table.js
Normal file
10
app/javascript/packs/vue/storage_locations_table.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { createApp } from 'vue/dist/vue.esm-bundler.js';
|
||||
import PerfectScrollbar from 'vue3-perfect-scrollbar';
|
||||
import StorageLocations from '../../vue/storage_locations/table.vue';
|
||||
import { mountWithTurbolinks } from './helpers/turbolinks.js';
|
||||
|
||||
const app = createApp();
|
||||
app.component('StorageLocations', StorageLocations);
|
||||
app.config.globalProperties.i18n = window.I18n;
|
||||
app.use(PerfectScrollbar);
|
||||
mountWithTurbolinks(app, '#storageLocationsTable');
|
143
app/javascript/vue/storage_locations/table.vue
Normal file
143
app/javascript/vue/storage_locations/table.vue
Normal file
|
@ -0,0 +1,143 @@
|
|||
<template>
|
||||
<div class="h-full">
|
||||
<DataTable :columnDefs="columnDefs"
|
||||
tableId="StorageLocationsTable"
|
||||
:dataUrl="dataSource"
|
||||
:reloadingTable="reloadingTable"
|
||||
:toolbarActions="toolbarActions"
|
||||
:actionsUrl="actionsUrl"
|
||||
@archive="archive"
|
||||
@restore="restore"
|
||||
@delete="deleteRepository"
|
||||
@update="update"
|
||||
@duplicate="duplicate"
|
||||
@export="exportRepositories"
|
||||
@share="share"
|
||||
@create="newRepository = true"
|
||||
@tableReloaded="reloadingTable = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global */
|
||||
|
||||
import DataTable from '../shared/datatable/table.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoriesTable',
|
||||
components: {
|
||||
DataTable
|
||||
},
|
||||
props: {
|
||||
dataSource: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
actionsUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
createUrl: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reloadingTable: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
columnDefs() {
|
||||
const columns = [{
|
||||
field: 'name',
|
||||
headerName: this.i18n.t('storage_locations.index.table.name'),
|
||||
sortable: true,
|
||||
notSelectable: true,
|
||||
cellRenderer: this.nameRenderer
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
headerName: this.i18n.t('storage_locations.index.table.id'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'sub_locations',
|
||||
headerName: this.i18n.t('storage_locations.index.table.sub_locations'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'items',
|
||||
headerName: this.i18n.t('storage_locations.index.table.items'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'free_spaces',
|
||||
headerName: this.i18n.t('storage_locations.index.table.free_spaces'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'shared',
|
||||
headerName: this.i18n.t('storage_locations.index.table.shared'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'owned_by',
|
||||
headerName: this.i18n.t('storage_locations.index.table.owned_by'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'created_on',
|
||||
headerName: this.i18n.t('storage_locations.index.table.created_on'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
headerName: this.i18n.t('storage_locations.index.table.description'),
|
||||
sortable: true
|
||||
}];
|
||||
|
||||
return columns;
|
||||
},
|
||||
toolbarActions() {
|
||||
const left = [];
|
||||
if (this.createUrl) {
|
||||
left.push({
|
||||
name: 'create_location',
|
||||
icon: 'sn-icon sn-icon-new-task',
|
||||
label: this.i18n.t('storage_locations.index.new_location'),
|
||||
type: 'emit',
|
||||
path: this.createUrl,
|
||||
buttonStyle: 'btn btn-primary'
|
||||
});
|
||||
left.push({
|
||||
name: 'create_box',
|
||||
icon: 'sn-icon sn-icon-item',
|
||||
label: this.i18n.t('storage_locations.index.new_box'),
|
||||
type: 'emit',
|
||||
path: this.createUrl,
|
||||
buttonStyle: 'btn btn-secondary'
|
||||
});
|
||||
}
|
||||
return {
|
||||
left,
|
||||
right: []
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Renderers
|
||||
nameRenderer(params) {
|
||||
const {
|
||||
name,
|
||||
urls
|
||||
} = params.data;
|
||||
return `<a class="hover:no-underline flex items-center gap-1"
|
||||
title="${name}" href="${urls.show}">
|
||||
<span class="truncate">${name}</span>
|
||||
</a>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
|
@ -43,6 +43,10 @@ Canaid::Permissions.register_for(Team) do
|
|||
within_limits && team.permission_granted?(user, TeamPermissions::INVENTORIES_CREATE)
|
||||
end
|
||||
|
||||
can :create_storage_locations do |user, team|
|
||||
true # TODO: Add permission check
|
||||
end
|
||||
|
||||
can :create_reports do |user, team|
|
||||
team.permission_granted?(user, TeamPermissions::REPORTS_CREATE)
|
||||
end
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
module Lists
|
||||
class StorageLocationSerializer < ActiveModel::Serializer
|
||||
attributes :id, :code, :name, :container, :description, :owned_by, :created_by, :created_on
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
attributes :id, :code, :name, :container, :description, :owned_by, :created_by, :created_on, :urls
|
||||
|
||||
def owned_by
|
||||
object.team.name
|
||||
|
@ -15,5 +17,11 @@ module Lists
|
|||
def created_on
|
||||
I18n.l(object.created_at, format: :full)
|
||||
end
|
||||
|
||||
def urls
|
||||
{
|
||||
show: storage_locations_path(parent_id: object.id),
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
23
app/views/storage_locations/index.html.erb
Normal file
23
app/views/storage_locations/index.html.erb
Normal file
|
@ -0,0 +1,23 @@
|
|||
<% provide(:head_title, t("storage_locations.index.head_title")) %>
|
||||
<% provide(:container_class, "no-second-nav-container") %>
|
||||
|
||||
<% if current_team %>
|
||||
<div class="content-pane flexible">
|
||||
<div class="content-header">
|
||||
<div class="title-row">
|
||||
<h1><%= t('storage_locations.index.head_title') %></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-body" data-e2e="e2e-CO-storage_-ocations">
|
||||
<div id="storageLocationsTable" class="fixed-content-body">
|
||||
<storage-locations
|
||||
actions-url="<%= actions_toolbar_storage_locations_path(current_team) %>"
|
||||
data-source="<%= storage_locations_path(format: :json, parent_id: params[:parent_id]) %>"
|
||||
create-url="<%= storage_locations_path if can_create_storage_locations?(current_team) %>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= javascript_include_tag 'vue_storage_locations_table' %>
|
||||
<% end %>
|
|
@ -343,6 +343,8 @@ en:
|
|||
templates: "Templates"
|
||||
protocol: "Protocol"
|
||||
label: "Label"
|
||||
items: "Items"
|
||||
locations: "Locations"
|
||||
reports: "Reports"
|
||||
settings: "Settings"
|
||||
activities: "Activities"
|
||||
|
@ -2663,6 +2665,21 @@ en:
|
|||
repository_ledger_records:
|
||||
errors:
|
||||
my_module_references_missing: 'Task references are not set'
|
||||
storage_locations:
|
||||
index:
|
||||
head_title: "Locations"
|
||||
new_location: "New location"
|
||||
new_box: "New box"
|
||||
table:
|
||||
name: "Location name"
|
||||
id: "ID"
|
||||
sub_locations: "Sub-locations"
|
||||
items: "Items"
|
||||
free_spaces: "Free spaces"
|
||||
shared: "Shared"
|
||||
owned_by: "Owned by"
|
||||
created_on: "Created on"
|
||||
description: "Description"
|
||||
libraries:
|
||||
manange_modal_column_index:
|
||||
title: "Manage columns"
|
||||
|
@ -4302,6 +4319,7 @@ en:
|
|||
labels: "Label"
|
||||
teams: "All Teams"
|
||||
addons: "Add-ons"
|
||||
locations: "Locations"
|
||||
label_printer: "Label printer"
|
||||
fluics_printer: "Fluics printer"
|
||||
|
||||
|
|
|
@ -808,6 +808,9 @@ Rails.application.routes.draw do
|
|||
resources :connected_devices, controller: 'users/connected_devices', only: %i(destroy)
|
||||
|
||||
resources :storage_locations, only: %i(index create destroy update) do
|
||||
collection do
|
||||
get :actions_toolbar
|
||||
end
|
||||
resources :storage_location_repository_rows, only: %i(index create destroy update)
|
||||
end
|
||||
|
||||
|
|
|
@ -65,7 +65,8 @@ const entryList = {
|
|||
vue_legacy_tags_modal: './app/javascript/packs/vue/legacy/tags_modal.js',
|
||||
vue_legacy_access_modal: './app/javascript/packs/vue/legacy/access_modal.js',
|
||||
vue_legacy_repository_menu_dropdown: './app/javascript/packs/vue/legacy/repository_menu_dropdown.js',
|
||||
vue_dashboard_new_task: './app/javascript/packs/vue/dashboard_new_task.js'
|
||||
vue_dashboard_new_task: './app/javascript/packs/vue/dashboard_new_task.js',
|
||||
vue_storage_locations_table: './app/javascript/packs/vue/storage_locations_table.js'
|
||||
};
|
||||
|
||||
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
||||
|
|
Loading…
Reference in a new issue