mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
commit
dfd7a439a6
@ -215,7 +215,7 @@
|
||||
}
|
||||
|
||||
.treeview .list-group-item {
|
||||
padding: 10px 5px;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.treeview .list-group-item .indent {
|
||||
@ -539,7 +539,7 @@
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.breadcrump {
|
||||
.breadcrumb {
|
||||
margin-bottom: 5px;
|
||||
margin-left: 5px;
|
||||
margin-right: 10px;
|
||||
|
@ -239,6 +239,23 @@ class CategoryParameterList(generics.ListAPIView):
|
||||
return queryset
|
||||
|
||||
|
||||
class CategoryTree(generics.ListAPIView):
|
||||
"""
|
||||
API endpoint for accessing a list of PartCategory objects ready for rendering a tree.
|
||||
"""
|
||||
|
||||
queryset = PartCategory.objects.all()
|
||||
serializer_class = part_serializers.CategoryTree
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
# Order by tree level (top levels first) and then name
|
||||
ordering = ['level', 'name']
|
||||
|
||||
|
||||
class PartSalePriceList(generics.ListCreateAPIView):
|
||||
"""
|
||||
API endpoint for list view of PartSalePriceBreak model
|
||||
@ -1515,6 +1532,7 @@ part_api_urls = [
|
||||
|
||||
# Base URL for PartCategory API endpoints
|
||||
url(r'^category/', include([
|
||||
url(r'^tree/', CategoryTree.as_view(), name='api-part-category-tree'),
|
||||
url(r'^parameters/', CategoryParameterList.as_view(), name='api-part-category-parameter-list'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
|
||||
|
@ -70,6 +70,20 @@ class CategorySerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class CategoryTree(InvenTreeModelSerializer):
|
||||
"""
|
||||
Serializer for PartCategory tree
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = PartCategory
|
||||
fields = [
|
||||
'pk',
|
||||
'name',
|
||||
'parent',
|
||||
]
|
||||
|
||||
|
||||
class PartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""
|
||||
Serializer for the PartAttachment class
|
||||
|
@ -6,6 +6,10 @@
|
||||
{% include 'part/category_sidebar.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_tree %}
|
||||
<div id="breadcrumb-tree"></div>
|
||||
{% endblock breadcrumb_tree %}
|
||||
|
||||
{% block heading %}
|
||||
{% if category %}
|
||||
{% trans "Part Category" %}: {{ category.name }}
|
||||
@ -239,8 +243,24 @@
|
||||
|
||||
{% endif %}
|
||||
|
||||
// Enable left-hand navigation sidebar
|
||||
enableSidebar('category');
|
||||
|
||||
// Enable breadcrumb tree view
|
||||
enableBreadcrumbTree({
|
||||
label: 'category',
|
||||
url: '{% url "api-part-category-tree" %}',
|
||||
{% if category %}
|
||||
selected: {{ category.pk }},
|
||||
{% endif %}
|
||||
processNode: function(node) {
|
||||
node.text = node.name;
|
||||
node.href = `/part/category/${node.pk}/`;
|
||||
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
loadPartCategoryTable(
|
||||
$('#subcategory-table'), {
|
||||
params: {
|
||||
|
@ -9,6 +9,10 @@
|
||||
{% include 'part/part_sidebar.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_tree %}
|
||||
<div id="breadcrumb-tree"></div>
|
||||
{% endblock breadcrumb_tree %}
|
||||
|
||||
{% block page_content %}
|
||||
|
||||
<div class='panel panel-hidden' id='panel-part-stock'>
|
||||
@ -132,10 +136,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='panel panel-hidden' id='panel-pricing'>
|
||||
<!-- TODO -->
|
||||
</div>
|
||||
|
||||
<div class='panel panel-hidden' id='panel-variants'>
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-wrap'>
|
||||
@ -1066,4 +1066,18 @@
|
||||
|
||||
enableSidebar('part');
|
||||
|
||||
enableBreadcrumbTree({
|
||||
label: 'part',
|
||||
url: '{% url "api-part-category-tree" %}',
|
||||
{% if part.category %}
|
||||
selected: {{ part.category.pk }},
|
||||
{% endif %}
|
||||
processNode: function(node) {
|
||||
node.text = node.name;
|
||||
node.href = `/part/category/${node.pk}/`;
|
||||
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
{% endblock %}
|
||||
|
@ -14,9 +14,10 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<a href='#' id='breadcrumb-tree-toggle' class="breadcrumb-item"><i class="fas fa-bars"></i></a>
|
||||
{% if part %}
|
||||
{% include "part/cat_link.html" with category=part.category part=part %}
|
||||
{% else %}
|
||||
{% include 'part/cat_link.html' with category=category %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock breadcrumbs %}
|
||||
|
@ -277,6 +277,24 @@ class StockLocationList(generics.ListCreateAPIView):
|
||||
]
|
||||
|
||||
|
||||
class StockLocationTree(generics.ListAPIView):
|
||||
"""
|
||||
API endpoint for accessing a list of StockLocation objects,
|
||||
ready for rendering as a tree
|
||||
"""
|
||||
|
||||
queryset = StockLocation.objects.all()
|
||||
serializer_class = StockSerializers.LocationTreeSerializer
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
# Order by tree level (top levels first) and then name
|
||||
ordering = ['level', 'name']
|
||||
|
||||
|
||||
class StockFilter(rest_filters.FilterSet):
|
||||
"""
|
||||
FilterSet for StockItem LIST API
|
||||
@ -1182,6 +1200,9 @@ class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
stock_api_urls = [
|
||||
url(r'^location/', include([
|
||||
|
||||
url(r'^tree/', StockLocationTree.as_view(), name='api-location-tree'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/', LocationDetail.as_view(), name='api-location-detail'),
|
||||
url(r'^.*$', StockLocationList.as_view(), name='api-location-list'),
|
||||
])),
|
||||
|
@ -390,6 +390,20 @@ class SerializeStockItemSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
|
||||
class LocationTreeSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
"""
|
||||
Serializer for a simple tree view
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = StockLocation
|
||||
fields = [
|
||||
'pk',
|
||||
'name',
|
||||
'parent',
|
||||
]
|
||||
|
||||
|
||||
class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
""" Detailed information about a stock location
|
||||
"""
|
||||
|
@ -9,9 +9,15 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<a href='#' id='breadcrumb-tree-toggle' class="breadcrumb-item"><i class="fas fa-bars"></i></a>
|
||||
{% include 'stock/loc_link.html' with location=item.location %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_tree %}
|
||||
<div id="breadcrumb-tree"></div>
|
||||
{% endblock breadcrumb_tree %}
|
||||
|
||||
|
||||
{% block heading %}
|
||||
{% trans "Stock Item" %}: {{ item.part.full_name}}
|
||||
{% endblock heading %}
|
||||
@ -611,4 +617,18 @@ $('#serial-number-search').click(function() {
|
||||
findStockItemBySerialNumber({{ item.part.pk }});
|
||||
});
|
||||
|
||||
enableBreadcrumbTree({
|
||||
label: 'stockitem',
|
||||
url: '{% url "api-location-tree" %}',
|
||||
{% if item.location %}
|
||||
selected: {{ item.location.pk }},
|
||||
{% endif %}
|
||||
processNode: function(node) {
|
||||
node.text = node.name;
|
||||
node.href = `/stock/item/${node.pk}/`;
|
||||
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
{% endblock %}
|
||||
|
@ -7,6 +7,10 @@
|
||||
{% include "stock/location_sidebar.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_tree %}
|
||||
<div id="breadcrumb-tree"></div>
|
||||
{% endblock breadcrumb_tree %}
|
||||
|
||||
{% block heading %}
|
||||
{% if location %}
|
||||
{% trans "Stock Location" %}: {{ location.name }}
|
||||
@ -348,4 +352,19 @@
|
||||
|
||||
enableSidebar('stocklocation');
|
||||
|
||||
// Enable breadcrumb tree view
|
||||
enableBreadcrumbTree({
|
||||
label: 'location',
|
||||
url: '{% url "api-location-tree" %}',
|
||||
{% if location %}
|
||||
selected: {{ location.pk }},
|
||||
{% endif %}
|
||||
processNode: function(node) {
|
||||
node.text = node.name;
|
||||
node.href = `/stock/location/${node.pk}/`;
|
||||
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
{% endblock %}
|
||||
|
@ -18,9 +18,10 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<a href='#' id='breadcrumb-tree-toggle' class="breadcrumb-item"><i class="fas fa-bars"></i></a>
|
||||
{% if item %}
|
||||
{% include 'stock/loc_link.html' with location=item.location %}
|
||||
{% else %}
|
||||
{% include 'stock/loc_link.html' with location=location %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock breadcrumbs %}
|
||||
|
@ -74,13 +74,13 @@
|
||||
|
||||
<div class='row flex-nowrap inventree-body'>
|
||||
<div class='col-auto px-1 sidebar-wrapper'>
|
||||
<div id='sidebar' class='collapse collapse-horizontal show border-end' style='display: none;'>
|
||||
<div id='sidebar' class='collapse collapse-horizontal show' style='display: none;'>
|
||||
<div id='sidebar-nav' class='list-group text-sm-start'>
|
||||
<ul id='sidebar-list-group' class='list-group sidebar-list-group'>
|
||||
{% block sidebar %}
|
||||
<!-- Sidebar goes here -->
|
||||
{% endblock %}
|
||||
{% include "sidebar_toggle.html" %}
|
||||
{% include "sidebar_toggle.html" with target='sidebar' %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -104,14 +104,20 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_list %}
|
||||
<div class='container-fluid navigation'>
|
||||
<div class='container-fluid navigation' id='breadcrumb-div'>
|
||||
<nav aria-label='breadcrumb'>
|
||||
<ol class='breadcrumb'>
|
||||
<ol class='breadcrumb' id='breadcrumb-list'>
|
||||
{% block breadcrumbs %}
|
||||
{% endblock %}
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div id='breadcrumb-tree-collapse' class='collapse collapse-horizontal show border' style='display: none;'>
|
||||
{% block breadcrumb_tree %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -6,6 +6,7 @@
|
||||
addSidebarHeader,
|
||||
addSidebarItem,
|
||||
addSidebarLink,
|
||||
enableBreadcrumbTree,
|
||||
enableSidebar,
|
||||
onPanelLoad,
|
||||
*/
|
||||
@ -145,6 +146,101 @@ function enableSidebar(label, options={}) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable support for breadcrumb tree navigation on this page
|
||||
*/
|
||||
function enableBreadcrumbTree(options) {
|
||||
|
||||
var label = options.label;
|
||||
|
||||
if (!label) {
|
||||
console.log('ERROR: enableBreadcrumbTree called without supplying label');
|
||||
return;
|
||||
}
|
||||
|
||||
var filters = options.filters || {};
|
||||
|
||||
inventreeGet(
|
||||
options.url,
|
||||
filters,
|
||||
{
|
||||
success: function(data) {
|
||||
|
||||
// Data are returned from the InvenTree server as a flattened list;
|
||||
// We need to convert this into a tree structure
|
||||
|
||||
var nodes = {};
|
||||
var roots = [];
|
||||
var node = null;
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
node = data[i];
|
||||
node.nodes = [];
|
||||
nodes[node.pk] = node;
|
||||
node.selectable = false;
|
||||
|
||||
if (options.processNode) {
|
||||
node = options.processNode(node);
|
||||
}
|
||||
|
||||
node.state = {
|
||||
expanded: node.pk == options.selected,
|
||||
selected: node.pk == options.selected,
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
node = data[i];
|
||||
|
||||
if (node.parent != null) {
|
||||
nodes[node.parent].nodes.push(node);
|
||||
|
||||
if (node.state.expanded) {
|
||||
nodes[node.parent].state.expanded = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
roots.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
$('#breadcrumb-tree').treeview({
|
||||
data: roots,
|
||||
showTags: true,
|
||||
enableLinks: true,
|
||||
expandIcon: 'fas fa-chevron-right',
|
||||
collapseIcon: 'fa fa-chevron-down',
|
||||
});
|
||||
|
||||
setBreadcrumbTreeState(label, state);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$('#breadcrumb-tree-toggle').click(function() {
|
||||
// Add callback to "collapse" and "expand" the sidebar
|
||||
|
||||
// By default, the menu is "expanded"
|
||||
var state = localStorage.getItem(`inventree-tree-state-${label}`) || 'expanded';
|
||||
|
||||
// We wish to "toggle" the state!
|
||||
setBreadcrumbTreeState(label, state == 'expanded' ? 'collapsed' : 'expanded');
|
||||
});
|
||||
|
||||
// Set the initial state (default = expanded)
|
||||
var state = localStorage.getItem(`inventree-tree-state-${label}`) || 'expanded';
|
||||
|
||||
function setBreadcrumbTreeState(label, state) {
|
||||
|
||||
if (state == 'collapsed') {
|
||||
$('#breadcrumb-tree-collapse').hide(100);
|
||||
} else {
|
||||
$('#breadcrumb-tree-collapse').show(100);
|
||||
}
|
||||
|
||||
localStorage.setItem(`inventree-tree-state-${label}`, state);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the "toggle" state of the sidebar
|
||||
@ -180,7 +276,7 @@ function setSidebarState(label, state) {
|
||||
function addSidebarItem(options={}) {
|
||||
|
||||
var html = `
|
||||
<a href='#' id='select-${options.label}' title='${options.text}' class='list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate sidebar-selector' data-bs-parent='#sidebar'>
|
||||
<a href='#' id='select-${options.label}' title='${options.text}' class='list-group-item sidebar-list-group-item border-end d-inline-block text-truncate sidebar-selector' data-bs-parent='#sidebar'>
|
||||
<i class='bi bi-bootstrap'></i>
|
||||
${options.content_before || ''}
|
||||
<span class='sidebar-item-icon fas ${options.icon}'></span>
|
||||
@ -199,7 +295,7 @@ function addSidebarItem(options={}) {
|
||||
function addSidebarHeader(options={}) {
|
||||
|
||||
var html = `
|
||||
<span title='${options.text}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate" data-bs-parent="#sidebar">
|
||||
<span title='${options.text}' class="list-group-item sidebar-list-group-item border-end d-inline-block text-truncate" data-bs-parent="#sidebar">
|
||||
<h6>
|
||||
<i class="bi bi-bootstrap"></i>
|
||||
<span class='sidebar-item-text' style='display: none;'>${options.text}</span>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<span title='{{ text }}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate bg-light" data-bs-parent="#sidebar">
|
||||
<span title='{{ text }}' class="list-group-item sidebar-list-group-item border-end d-inline-block text-truncate bg-light" data-bs-parent="#sidebar">
|
||||
<h6>
|
||||
<i class="bi bi-bootstrap"></i>
|
||||
{% if icon %}<span class='sidebar-item-icon fas {{ icon }}'></span>{% endif %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<a href="#" id='select-{{ label }}' title='{{ text }}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate sidebar-selector" data-bs-parent="#sidebar">
|
||||
<a href="#" id='select-{{ label }}' title='{{ text }}' class="list-group-item sidebar-list-group-item border-end d-inline-block text-truncate sidebar-selector" data-bs-parent="#sidebar">
|
||||
<i class="bi bi-bootstrap"></i>
|
||||
<span class='sidebar-item-icon fas {{ icon|default:"fa-circle" }}'></span>
|
||||
<span class='sidebar-item-text' style='display: none;'>{{ text }}</span>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% load i18n %}
|
||||
<a href="{{ url }}" class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate" data-bs-parent="#sidebar">
|
||||
<a href="{{ url }}" class="list-group-item sidebar-list-group-item border-end d-inline-block text-truncate" data-bs-parent="#sidebar">
|
||||
<i class="bi bi-bootstrap"></i><span class='sidebar-item-icon fas {{ icon }}'></span><span class='sidebar-item-text' style='display: none;'>{{ text }}</span>
|
||||
</a>
|
||||
|
@ -1,3 +1,4 @@
|
||||
<a href="#" id='sidebar-toggle' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate sidebar-toggle" data-bs-parent="#sidebar" style='display: none;'>
|
||||
<a href="#" id='{{ target }}-toggle' class="list-group-item sidebar-list-group-item border-end d-inline-block text-truncate sidebar-toggle" data-bs-parent="#sidebar" style='display: none;'>
|
||||
<i class="bi bi-bootstrap"></i><span id='sidebar-toggle-icon' class='sidebar-item-icon fas fa-chevron-left'></span>
|
||||
{% if text %}<span class='sidebar-item-text' style='display: none;'>{{ text }}</span>{% endif %}
|
||||
</a>
|
||||
|
Loading…
Reference in New Issue
Block a user