├── .eslintrc ├── .prettierrc ├── LICENSE.md ├── README.md ├── composer.json ├── dist └── js │ └── tool.js ├── package.json ├── resources ├── js │ ├── components │ │ └── Tool.vue │ └── tool.js └── views │ └── nova-overrides │ └── resources │ └── navigation.blade.php ├── screenshots └── example.gif ├── src ├── Categorise.php ├── NovaCategorise.php └── ToolServiceProvider.php ├── webpack.mix.js └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:vue/essential", 5 | "prettier" 6 | ], 7 | "parserOptions": { 8 | "ecmaVersion": 2017 9 | }, 10 | "globals": { 11 | "Nova": true 12 | }, 13 | "env": { 14 | "browser": true, 15 | "node": true 16 | }, 17 | "rules": { 18 | "vue/html-indent": ["error", 4] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "tabWidth": 4, 5 | "trailingComma": "es5" 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) AlexBowers bvba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | This is now covered in Nova 1.0.18+ so does not need a package for it: 4 | 5 | ```php 6 | public static $group = 'name'; // you may also use a public static method of the same name 7 | ``` 8 | 9 | You may continue to use this package if you want to be able to collapse the groups, however I'd recommend going with the core features of Nova. 10 | 11 | # Group and categorise your nova resources 12 | 13 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/alexbowers/nova-categorise-resources.svg?style=flat-square)](https://packagist.org/packages/alexbowers/nova-categorise-resources) 14 | [![Quality Score](https://img.shields.io/scrutinizer/g/alexbowers/nova-categorise-resources.svg?style=flat-square)](https://scrutinizer-ci.com/g/alexbowers/nova-categorise-resources) 15 | [![Total Downloads](https://img.shields.io/packagist/dt/alexbowers/nova-categorise-resources.svg?style=flat-square)](https://packagist.org/packages/alexbowers/nova-categorise-resources) 16 | 17 | 18 | 19 | A nova resource can be given a category name. 20 | 21 | If a category name is provided, then that will act as a label. 22 | 23 | The label will be standardised, so any changes in capitalisation will be removed 24 | and it will be converted into Title case. 25 | 26 | For example: 27 | 28 | "Customers Information", "customers information" and "cUStoMERs INFORMATION" 29 | 30 | all become "Customers Information" 31 | 32 | If the category is left empty, then all empty resources will get grouped together. 33 | 34 | If there is only one category, it will not be collapsable, but will instead have a label above it all 35 | 36 | if there is only one category, and it is empty, then it will act as Nova does by default. 37 | 38 | ![Categorise Resources Example](https://github.com/alexbowers/nova-categorise-resources/blob/master/screenshots/example.gif?raw=true) 39 | 40 | ## Installation 41 | 42 | You can install the package in to a Laravel app that uses [Nova](https://nova.laravel.com) via composer: 43 | 44 | ```bash 45 | composer require alexbowers/nova-categorise-resources 46 | ``` 47 | 48 | ## Usage 49 | 50 | In any / all of your resources add 51 | 52 | ```php 53 | public static $category = "Your Category label"; 54 | ``` 55 | 56 | ### Security 57 | 58 | If you discover any security related issues, please email bowersbros@gmail.com instead of using the issue tracker. 59 | 60 | ## Credits 61 | 62 | - [Alex Bowers](https://github.com/alexbowers) 63 | 64 | ## License 65 | 66 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 67 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alexbowers/nova-categorise-resources", 3 | "description": "Group and categorise your nova resources", 4 | "keywords": [ 5 | "laravel", 6 | "nova" 7 | ], 8 | "homepage": "https://github.com/alexbowers/nova-categorise-resources", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Alex Bowers", 13 | "email": "bowersbros@gmail.com", 14 | "role": "Developer" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.1.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "AlexBowers\\NovaCategoriseResources\\": "src/" 23 | } 24 | }, 25 | "extra": { 26 | "laravel": { 27 | "providers": [ 28 | "AlexBowers\\NovaCategoriseResources\\ToolServiceProvider" 29 | ] 30 | } 31 | }, 32 | "config": { 33 | "sort-packages": true 34 | }, 35 | "minimum-stability": "dev", 36 | "prefer-stable": true 37 | } 38 | -------------------------------------------------------------------------------- /dist/js/tool.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(2),o=n.n(r);Nova.booting(function(e,t){e.component("grouped-resource-collapsable",o.a)})},function(e,t,n){var r=n(3)(n(4),n(5),!1,null,null,null);e.exports=r.exports},function(e,t){e.exports=function(e,t,n,r,o,s){var i,a=e=e||{},u=typeof e.default;"object"!==u&&"function"!==u||(i=e,a=e.default);var c,l="function"==typeof a?a.options:a;if(t&&(l.render=t.render,l.staticRenderFns=t.staticRenderFns,l._compiled=!0),n&&(l.functional=!0),o&&(l._scopeId=o),s?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),r&&r.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(s)},l._ssrRegister=c):r&&(c=r),c){var d=l.functional,f=d?l.render:l.beforeCreate;d?(l._injectStyles=c,l.render=function(e,t){return c.call(t),f(e,t)}):l.beforeCreate=f?[].concat(f,c):[c]}return{esModule:i,exports:a,options:l}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{header:String,last:Boolean},data:function(){return{expanded:!0}},methods:{toggle:function(){this.expanded=!this.expanded}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",[n("h4",{ref:"nova-category",staticClass:"ml-6 leading-wide font-bold text-white-50% mb-2 text-sm capitalize cursor-pointer hover:text-white",on:{click:function(t){return t.preventDefault(),e.toggle(t)}}},[e._v("\n "+e._s(e.header)+"\n "),e.expanded?[e._v("−")]:[e._v("+")]],2),e._v(" "),n("transition-group",{staticClass:"list-reset",class:{"mb-6":e.last},attrs:{tag:"ul"}},[e.expanded?e._t("default"):e._e()],2)],1)},staticRenderFns:[]}}]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 11 | "check-format": "prettier --list-different 'resources/**/*.{css,js,vue}'", 12 | "format": "prettier --write 'resources/**/*.{css,js,vue}'", 13 | "lint": "eslint resources/js --fix --ext js,vue" 14 | }, 15 | "devDependencies": { 16 | "cross-env": "^5.0.0", 17 | "eslint": "^4.19.1", 18 | "eslint-config-prettier": "^2.9.0", 19 | "eslint-plugin-vue": "^4.4.0", 20 | "laravel-mix": "^1.0", 21 | "prettier": "^1.14.0" 22 | }, 23 | "dependencies": { 24 | "animated-scroll-to": "^1.2.2", 25 | "vue": "^2.5.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/js/components/Tool.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 33 | -------------------------------------------------------------------------------- /resources/js/tool.js: -------------------------------------------------------------------------------- 1 | import Tool from './components/Tool'; 2 | 3 | Nova.booting((Vue, router) => { 4 | Vue.component('grouped-resource-collapsable', Tool); 5 | }); 6 | -------------------------------------------------------------------------------- /resources/views/nova-overrides/resources/navigation.blade.php: -------------------------------------------------------------------------------- 1 | @if (count(Nova::availableResources(request()))) 2 |

3 | 4 | 6 | 7 | {{ __('Resources') }} 8 |

9 | 10 | @foreach (\AlexBowers\NovaCategoriseResources\NovaCategorise::availableResourcesGrouped(request()) as $group => $resouces) 11 | 12 | 13 | @foreach ($resouces as $key => $resource) 14 |
  • 15 | 21 | {{ $resource::label() }} 22 | 23 |
  • 24 | @endforeach 25 |
    26 | @endforeach 27 | @endif 28 | -------------------------------------------------------------------------------- /screenshots/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbowers/nova-categorise-resources/1872d743f9c90dba9affe5b59b4edb5310311fe3/screenshots/example.gif -------------------------------------------------------------------------------- /src/Categorise.php: -------------------------------------------------------------------------------- 1 | filter(function ($resource) { 16 | return $resource::$displayInNavigation; 17 | })->groupBy(function ($resource) { 18 | if (property_exists($resource, 'category')) { 19 | return __(ucwords($resource::$category)); 20 | } 21 | 22 | return __('Other'); 23 | })->sortKeys(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ToolServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__ . '/../resources/views/nova-overrides', 'nova'); 19 | 20 | Nova::serving(function (ServingNova $event) { 21 | Nova::script('nova-categorise-resources', __DIR__ . '/../dist/js/tool.js'); 22 | }); 23 | } 24 | 25 | /** 26 | * Register any application services. 27 | * 28 | * @return void 29 | */ 30 | public function register() 31 | { 32 | // 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix.js('resources/js/tool.js', 'dist/js'); 4 | --------------------------------------------------------------------------------