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:
aignatov-bio 2024-07-17 14:50:51 +02:00 committed by GitHub
commit 33626e0ddd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 274 additions and 6 deletions

View file

@ -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

View file

@ -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

View 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');

View 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>

View file

@ -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

View file

@ -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

View 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 %>

View file

@ -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"

View file

@ -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

View file

@ -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