partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit f7894298b8c9ae45e18035f09398ac0f073ab71a
parent a7ace8f4631e287b4d7553b57ef8e7eb944d5e6a
Author: Felicitus <felicitus@felicitus.org>
Date:   Fri, 17 Jul 2015 00:38:51 +0200

Implemented new footprint category view

Diffstat:
Mapp/config/config.yml | 8++++----
Msrc/PartKeepr/FootprintBundle/Entity/Footprint.php | 4++--
Msrc/PartKeepr/FootprintBundle/Entity/FootprintCategory.php | 2+-
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorComponent.js | 1+
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorGrid.js | 428++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintEditorComponent.js | 35+++++++++--------------------------
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintGrid.js | 11+++++++++++
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintNavigation.js | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintTree.js | 80++++---------------------------------------------------------------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Ext.ux/SearchField.js | 5++++-
Msrc/PartKeepr/FrontendBundle/Resources/views/index.html.twig | 2++
11 files changed, 355 insertions(+), 313 deletions(-)

diff --git a/app/config/config.yml b/app/config/config.yml @@ -153,7 +153,7 @@ services: tags: [ { name: "api.resource" } ] calls: - method: "initFilters" - arguments: [ [ "@resource.order_filter" ] ] + arguments: [ [ "@resource.order_filter", "@doctrine_reflection_service.search_filter" ] ] - method: "initNormalizationContext" arguments: [ { groups: [ "default" ] } ] - method: "initDenormalizationContext" @@ -321,7 +321,7 @@ services: - method: "initItemOperations" arguments: [ [ "@resource.partmeasurementunit.item_operation.custom_put", "@resource.partmeasurementunit.item_operation.get", "@resource.partmeasurementunit.item_operation.put" ] ] - method: "initFilters" - arguments: [ [ "@resource.order_filter" ] ] + arguments: [ [ "@resource.order_filter", "@doctrine_reflection_service.search_filter" ] ] - method: "initNormalizationContext" arguments: [ { groups: [ "default" ] } ] - method: "initDenormalizationContext" @@ -335,7 +335,7 @@ services: tags: [ { name: "api.resource" } ] calls: - method: "initFilters" - arguments: [ [ "@resource.order_filter" ] ] + arguments: [ [ "@resource.order_filter", "@doctrine_reflection_service.search_filter" ] ] - method: "initNormalizationContext" arguments: [ { groups: [ "default" ] } ] - method: "initDenormalizationContext" @@ -348,7 +348,7 @@ services: tags: [ { name: "api.resource" } ] calls: - method: "initFilters" - arguments: [ [ "@resource.order_filter" ] ] + arguments: [ [ "@resource.order_filter", "@doctrine_reflection_service.search_filter" ] ] - method: "initNormalizationContext" arguments: [ { groups: [ "default" ] } ] - method: "initDenormalizationContext" diff --git a/src/PartKeepr/FootprintBundle/Entity/Footprint.php b/src/PartKeepr/FootprintBundle/Entity/Footprint.php @@ -10,7 +10,7 @@ use Symfony\Component\Serializer\Annotation\Groups; /** * @ORM\Entity - * @TargetService(uri="/api/footprint") + * @TargetService(uri="/api/footprints") */ class Footprint extends BaseEntity { @@ -83,7 +83,7 @@ class Footprint extends BaseEntity */ public function setName($name) { - //$this->name = $name; + $this->name = $name; } /** diff --git a/src/PartKeepr/FootprintBundle/Entity/FootprintCategory.php b/src/PartKeepr/FootprintBundle/Entity/FootprintCategory.php @@ -34,7 +34,7 @@ class FootprintCategory extends AbstractCategory /** * @ORM\OneToMany(targetEntity="Footprint", mappedBy="category") - * @Groups({"default"}) + * */ protected $footprints; diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorComponent.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorComponent.js @@ -89,6 +89,7 @@ Ext.define('PartKeepr.EditorComponent', { var editor = this.createEditor(this.newItemText); editor.newItem(defaults); + console.log(defaults); this.editorTabPanel.add(editor).show(); }, /** diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Editor/EditorGrid.js @@ -1,225 +1,247 @@ /** * This class extends a regular GridPanel with the following features: - * + * * - Buttons to add/delete items * - Enable/Disable the delete button if an item is selected * - Search field * - Paging Toolbar */ Ext.define('PartKeepr.EditorGrid', { - extend: 'PartKeepr.BaseGrid', - alias: 'widget.EditorGrid', - - /** + extend: 'PartKeepr.BaseGrid', + alias: 'widget.EditorGrid', + + /** * @cfg {String} text The text for the "delete" button */ - deleteButtonText: i18n("Delete Item"), - - /** + deleteButtonText: i18n("Delete Item"), + + /** * @cfg {String} text The path to the 'delete' icon */ - deleteButtonIcon: 'resources/silkicons/delete.png', - - /** + deleteButtonIcon: 'resources/silkicons/delete.png', + + /** * @cfg {String} text The text for the "add" button */ - addButtonText: i18n("Add Item"), - - /** + addButtonText: i18n("Add Item"), + + /** * @cfg {String} text The path to the 'add' icon */ - addButtonIcon: 'resources/silkicons/add.png', - - /** + addButtonIcon: 'resources/silkicons/add.png', + + /** * @cfg {Boolean} boolean Specifies whether to enable the top toolbar or not */ - enableTopToolbar: true, - - /** + enableTopToolbar: true, + + /** * @cfg {String} text Defines if the "add"/"delete" buttons should show their text or icon only. If "hide", the * button text is hidden, anything else shows the text. */ - buttonTextMode: 'hide', - - /** - * @cfg {Boolean} boolean Defines if the grid should automatically calculate it's page size - */ - automaticPageSize: false, - - /** - * @cfg {Integer} integer Defines the row height with which the calculator should assume - */ - automaticPageSizeRowHeight: 21, - - /** - * @cfg {Boolean} boolean Defines if the list should be read-only, or if the list can be edited. Defaults to true. - */ - enableEditing: true, - - /** - * @event itemSelect - * Fires if a record was selected within the grid. - * @param {Object} record The selected record - */ - initComponent: function () { - - /** - * @event itemDeselect - * Fires if a record was deselected within the grid. - * @param {Object} record The deselected record - */ - - /** - * @event itemEdit - * Fires if a record should be edited. - * @param {Object} record The record to edit - */ - - /** - * @event itemDelete - * Fires if the delete button was clicked. - */ - - /** - * @event itemDelete - * Fires if the add button was clicked. - */ - - - this.on("itemclick", this._onItemEdit, this); - - this.deleteButton = Ext.create("Ext.button.Button", { - text: (this.buttonTextMode !== "hide") ? this.deleteButtonText : '', - tooltip: this.deleteButtonText, - icon: this.deleteButtonIcon, - handler: Ext.bind(function () { - this.fireEvent("itemDelete"); - }, this), - disabled: true - }); - - this.addButton = Ext.create("Ext.button.Button", { - text: (this.buttonTextMode !== "hide") ? this.addButtonText : '', - tooltip: this.addButtonText, - icon: this.addButtonIcon, - handler: Ext.bind(function () { - this.fireEvent("itemAdd"); - }, this) - }); - - this.searchField = Ext.create("Ext.ux.form.SearchField",{ - store: this.store, - targetField: 'name' - }); - - var topToolbarItems = []; - - if (this.enableEditing) { - topToolbarItems.push(this.addButton); - topToolbarItems.push(this.deleteButton); - } - - topToolbarItems.push({ xtype: 'tbfill' }); - topToolbarItems.push(this.searchField); - - this.topToolbar = Ext.create("Ext.toolbar.Toolbar",{ - dock: 'top', - enableOverflow: true, - items: topToolbarItems - }); - - this.bottomToolbar = Ext.create("Ext.toolbar.Paging", { - store: this.store, - enableOverflow: true, - dock: 'bottom', - displayInfo: false - }); - - this.dockedItems = new Array(); - - this.dockedItems.push(this.bottomToolbar); - - if (this.enableTopToolbar) { - this.dockedItems.push(this.topToolbar); - } - - if (!Ext.isArray(this.plugins)) { - this.plugins = []; - } - - this.plugins.push('gridmenu'); - - this.callParent(); - - this.getSelectionModel().on("select", this._onItemSelect, this); - this.getSelectionModel().on("deselect", this._onItemDeselect, this); - - if (this.automaticPageSize) { - this.on("resize", this.reassignPageSize, this); - } - }, - /** - * Re-calculates and re-assigns the page size for the assigned store. - * - * Automatically reloads the store. - * - * @param none - * @return nothing - */ - reassignPageSize: function () { - if (this.store.isLoading()) { return; } - if (this.getView().getHeight() === 0) { return; } - - var numRecords = Math.floor(this.getView().getHeight() / this.automaticPageSizeRowHeight); - - if (numRecords < 1) { numRecords = 1; } - - var oldStartIndex = this.store.pageSize * this.store.currentPage; - - this.store.pageSize = numRecords; - - var newStartPage = Math.floor(oldStartIndex / numRecords); - - if (newStartPage < 1) { - newStartPage = 1; - } - - this.store.loadPage(newStartPage); - }, - syncChanges: function (record) { - // Simply reload the store for now - this.store.load(); - }, - /** - * Called when an item was selected. Enables/disables the delete button. - */ - _updateDeleteButton: function (selectionModel, record) { - /* Right now, we support delete on a single record only */ - if (this.getSelectionModel().getCount() == 1) { - this.deleteButton.enable(); - } else { - this.deleteButton.disable(); - } - }, - - /** - * Called when an item should be edited - */ - _onItemEdit: function (view, record) { - this.fireEvent("itemEdit", record.getId()); - }, - /** - * Called when an item was selected - */ - _onItemSelect: function (selectionModel, record) { - this._updateDeleteButton(selectionModel, record); - this.fireEvent("itemSelect", record); - }, - /** - * Called when an item was deselected - */ - _onItemDeselect: function (selectionModel, record) { - this._updateDeleteButton(selectionModel, record); - this.fireEvent("itemDeselect", record); - } + buttonTextMode: 'hide', + + /** + * @cfg {Boolean} boolean Defines if the grid should automatically calculate it's page size + */ + automaticPageSize: false, + + /** + * @cfg {Integer} integer Defines the row height with which the calculator should assume + */ + automaticPageSizeRowHeight: 21, + + /** + * @cfg {Boolean} boolean Defines if the list should be read-only, or if the list can be edited. Defaults to true. + */ + enableEditing: true, + + listeners: { + 'reconfigure': 'onReconfigure' + }, + /** + * @event itemSelect + * Fires if a record was selected within the grid. + * @param {Object} record The selected record + */ + initComponent: function () + { + + /** + * @event itemDeselect + * Fires if a record was deselected within the grid. + * @param {Object} record The deselected record + */ + + /** + * @event itemEdit + * Fires if a record should be edited. + * @param {Object} record The record to edit + */ + + /** + * @event itemDelete + * Fires if the delete button was clicked. + */ + + /** + * @event itemAdd + * Fires if the add button was clicked. + */ + + this.on("itemclick", this._onItemEdit, this); + + this.deleteButton = Ext.create("Ext.button.Button", { + text: (this.buttonTextMode !== "hide") ? this.deleteButtonText : '', + tooltip: this.deleteButtonText, + icon: this.deleteButtonIcon, + handler: Ext.bind(function () + { + this.fireEvent("itemDelete"); + }, this), + disabled: true + }); + + this.addButton = Ext.create("Ext.button.Button", { + text: (this.buttonTextMode !== "hide") ? this.addButtonText : '', + tooltip: this.addButtonText, + icon: this.addButtonIcon, + handler: Ext.bind(function () + { + this.fireEvent("itemAdd"); + }, this) + }); + + this.searchField = Ext.create("Ext.ux.form.SearchField", { + store: this.store, + targetField: 'name' + }); + + var topToolbarItems = []; + + if (this.enableEditing) { + topToolbarItems.push(this.addButton); + topToolbarItems.push(this.deleteButton); + } + + topToolbarItems.push({xtype: 'tbfill'}); + topToolbarItems.push(this.searchField); + + this.topToolbar = Ext.create("Ext.toolbar.Toolbar", { + dock: 'top', + enableOverflow: true, + items: topToolbarItems + }); + + this.bottomToolbar = Ext.create("Ext.toolbar.Paging", { + store: this.store, + enableOverflow: true, + dock: 'bottom', + displayInfo: false + }); + + this.dockedItems = new Array(); + + this.dockedItems.push(this.bottomToolbar); + + if (this.enableTopToolbar) { + this.dockedItems.push(this.topToolbar); + } + + if (!Ext.isArray(this.plugins)) { + this.plugins = []; + } + + this.plugins.push('gridmenu'); + + this.callParent(); + + this.getSelectionModel().on("select", this._onItemSelect, this); + this.getSelectionModel().on("deselect", this._onItemDeselect, this); + + if (this.automaticPageSize) { + this.on("resize", this.reassignPageSize, this); + } + }, + /** + * Re-calculates and re-assigns the page size for the assigned store. + * + * Automatically reloads the store. + * + * @param none + * @return nothing + */ + reassignPageSize: function () + { + if (this.store.isLoading()) { + return; + } + if (this.getView().getHeight() === 0) { + return; + } + + var numRecords = Math.floor(this.getView().getHeight() / this.automaticPageSizeRowHeight); + + if (numRecords < 1) { + numRecords = 1; + } + + var oldStartIndex = this.store.pageSize * this.store.currentPage; + + this.store.pageSize = numRecords; + + var newStartPage = Math.floor(oldStartIndex / numRecords); + + if (newStartPage < 1) { + newStartPage = 1; + } + + this.store.loadPage(newStartPage); + }, + onReconfigure: function (me, store) { + this.searchField.setStore(store); + this.bottomToolbar.setStore(store); + + }, + syncChanges: function (record) + { + // Simply reload the store for now + this.store.load(); + }, + /** + * Called when an item was selected. Enables/disables the delete button. + */ + _updateDeleteButton: function (selectionModel, record) + { + /* Right now, we support delete on a single record only */ + if (this.getSelectionModel().getCount() == 1) { + this.deleteButton.enable(); + } else { + this.deleteButton.disable(); + } + }, + + /** + * Called when an item should be edited + */ + _onItemEdit: function (view, record) + { + this.fireEvent("itemEdit", record.getId()); + }, + /** + * Called when an item was selected + */ + _onItemSelect: function (selectionModel, record) + { + this._updateDeleteButton(selectionModel, record); + this.fireEvent("itemSelect", record); + }, + /** + * Called when an item was deselected + */ + _onItemDeselect: function (selectionModel, record) + { + this._updateDeleteButton(selectionModel, record); + this.fireEvent("itemDeselect", record); + } }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintEditorComponent.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintEditorComponent.js @@ -1,39 +1,22 @@ Ext.define('PartKeepr.FootprintEditorComponent', { extend: 'PartKeepr.EditorComponent', alias: 'widget.FootprintEditorComponent', - navigationClass: 'PartKeepr.FootprintTree', + navigationClass: 'PartKeepr.FootprintNavigation', editorClass: 'PartKeepr.FootprintEditor', newItemText: i18n("New Footprint"), - model: 'PartKeepr.FootprintBundle.Entity.FootprintCategory', - storeType: "Ext.data.TreeStore", + model: 'PartKeepr.FootprintBundle.Entity.Footprint', initComponent: function () { - this.createStore( - { - remoteSort: false, - folderSort: true, - rootVisible: true, - autoLoad: true, - sorters: [{ - property: 'name', - direction: 'ASC' - }], - root: { - "@id": "/~felicitus/PartKeepr/web/app_dev.php/api/footprint_categories/1" - }, - model: "PartKeepr.FootprintBundle.Entity.FootprintCategory", - proxy: { - type: "Hydra", - appendId: false, - reader: { - type: 'json' - } - } - }); + this.createStore({ + sorters: [{ + property: 'name', + direction:'ASC' + }] + }); this.callParent(); }, deleteRecord: function (r) { - var editor = this.findEditor(r.get("id")); + var editor = this.findEditor(r.getId()); if (editor !== null) { this.editorTabPanel.remove(editor); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintGrid.js @@ -0,0 +1,10 @@ +Ext.define('PartKeepr.FootprintGrid', { + extend: 'PartKeepr.EditorGrid', + xtype: 'partkeepr.FootprintGrid', + columns: [ + {header: i18n("Footprint"), dataIndex: 'name', flex: 1} + ], + addButtonText: i18n("Add Footprint"), + deleteButtonText: i18n("Delete Footprint"), + automaticPageSize: true +});+ \ No newline at end of file diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintNavigation.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintNavigation.js @@ -0,0 +1,91 @@ +Ext.define("PartKeepr.FootprintNavigation", { + extend: 'Ext.panel.Panel', + + layout: 'border', + + /** + * @var {Ext.data.Store} + */ + store: null, + items: [ + { + xtype: 'partkeepr.FootprintTree', + region: 'center', + }, { + xtype: 'partkeepr.FootprintGrid', + resizable: true, + region: 'south', + height: "50%" + } + ], + + initComponent: function () { + this.callParent(arguments); + + this.treeStore = Ext.create("Ext.data.TreeStore", + { + remoteSort: false, + folderSort: true, + rootVisible: true, + autoLoad: true, + sorters: [{ + property: 'name', + direction: 'ASC' + }], + root: { + "@id": "/~felicitus/PartKeepr/web/app_dev.php/api/footprint_categories/1" + }, + model: "PartKeepr.FootprintBundle.Entity.FootprintCategory", + proxy: { + type: "Hydra", + appendId: false, + reader: { + type: 'json' + } + + }}); + + this.down("partkeepr\\.FootprintTree").setStore(this.treeStore); + this.down("partkeepr\\.FootprintTree").on("itemclick", this.onCategoryClick, this); + this.down("partkeepr\\.FootprintGrid").setStore(this.store); + this.down("partkeepr\\.FootprintGrid").on("itemAdd", this.onAddFootprint, this); + this.down("partkeepr\\.FootprintGrid").on("itemEdit", function (id) + { + this.fireEvent("itemEdit", id); + }, this + ); + + }, + onCategoryClick: function (tree, record) { + var filter = Ext.create("Ext.util.Filter", { + property: 'category', + operator: '=', + value: record.getId() + }); + + this.store.addFilter(filter); + }, + /** + * Called when a footprint is about to be added. This prepares the to-be-edited record with the proper category id. + */ + onAddFootprint: function () { + var selection = this.down("partkeepr\\.FootprintTree").getSelection(); + + var category; + if (selection.length == 0) { + category = this.down("partkeepr\\.FootprintTree").getRootNode().getId(); + } else { + var item = selection.shift(); + category = item.getId(); + } + + this.fireEvent("itemAdd", { + category: category + }); + }, + syncChanges: function () { + this.down("partkeepr\\.FootprintGrid").getStore().load(); + } + + +});+ \ No newline at end of file diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintTree.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Footprint/FootprintTree.js @@ -1,7 +1,7 @@ Ext.define("PartKeepr.FootprintTree", { extend: 'PartKeepr.CategoryEditorTree', alias: 'widget.FootprintTree', - + xtype: 'partkeepr.FootprintTree', ddGroup: 'FootprintTree', folderSort: true, @@ -23,11 +23,11 @@ Ext.define("PartKeepr.FootprintTree", { initComponent: function () { this.callParent(); - this.on("itemclick", Ext.bind(function (t,record) { + /*this.on("itemclick", Ext.bind(function (t,record) { if (record.self.getName() == "PartKeepr.Footprint") { this.fireEvent("itemEdit", record.get("id")); } - }, this)); + }, this));*/ this.addButton = Ext.create("Ext.button.Button", { @@ -51,32 +51,7 @@ Ext.define("PartKeepr.FootprintTree", { this.getSelectionModel().on("select", this._onItemSelect, this); this.getSelectionModel().on("deselect", this._onItemDeselect, this); }, - /** - * Called when a footprint is about to be added. This prepares the to-be-edited record with the proper category id. - */ - _onAddFootprint: function () { - var record = this.getSelectionModel().getLastSelected(); - - if (!record) { - // Nothing is selected, use the root node's ID as category - this.fireEvent("itemAdd", { category: this.getRootNode().get("id") }); - } - - if (record.self.getName() == "PartKeepr.FootprintCategory") { - // Selected node is a footprint category - this.fireEvent("itemAdd", { category: record.get("id") }); - } else { - // Selected node is a footprint - if (record.parentNode && record.parentNode.self.getName() == "PartKeepr.FootprintCategory") { - // Selected parent node is a category, perfect. Let's use this - this.fireEvent("itemAdd", { category: record.parentNode.get("id") }); - } else { - // Something went probably wrong, use the root node - this.fireEvent("itemAdd", { category: this.getRootNode().get("id") }); - } - - } - }, + /** * Called when an item was selected */ @@ -115,53 +90,6 @@ Ext.define("PartKeepr.FootprintTree", { this.loadCategories(); }, - /** - * Injects all footprints into the correct categories. That way, we don't have to implement a complete - * own tree, but rather take what's already there for the categories and extend that. - */ - _onCategoriesLoaded: function () { - this.callParent(arguments); - return; - - var store = PartKeepr.getApplication().getFootprintStore(); - var nodeData, record; - - Ext.data.NodeInterface.decorate("PartKeepr.FootprintBundle.Entity.FootprintCategory"); - - for (var i=0;i<store.getCount();i++) { - record = store.getAt(i); - nodeData = { - text: record.get("name"), - name: record.get("name"), - id: record.get("id"), - leaf: true, - iconCls:'icon-footprint' - }; - - var newNode = Ext.create("PartKeepr.Footprint", nodeData); - - if (record.get("category") === 0) { - this.getRootNode().firstChild.appendChild(newNode); - } else { - - var node = this.getRootNode().findChildBy(function () { - if (this.self.getName() == "PartKeepr.FootprintCategory" && this.get("id") == record.get("category")) { - return true; - } else { - return false; - } - }, false, true); - - if (node) { - node.appendChild(newNode); - } else { - this.getRootNode().firstChild.appendChild(newNode); - } - } - - } - - }, onBeforeDrop: function (node, data, overModel, dropPosition, dropFunction, options) { var draggedRecord = data.records[0]; var droppedOn = this.getView().getRecord(node); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Ext.ux/SearchField.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Ext.ux/SearchField.js @@ -106,5 +106,8 @@ Ext.define('Ext.ux.form.SearchField', { me.hasSearch = true; this.getTrigger("clear").show(); - } + }, + setStore: function (store) { + this.store = store; + } }); \ No newline at end of file diff --git a/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig b/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig @@ -140,6 +140,8 @@ '@PartKeeprFrontendBundle/Resources/public/js/Components/PartMeasurementUnit/PartMeasurementUnitEditorComponent.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Unit/UnitEditorComponent.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Footprint/FootprintEditorComponent.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/Footprint/FootprintNavigation.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/Footprint/FootprintGrid.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/User/UserEditorComponent.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/SystemNotice/SystemNoticeEditorComponent.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/StorageLocation/StorageLocationEditorComponent.js'