├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── dist
├── js
│ └── dynamic-action-fields.js
└── mix-manifest.json
├── package.json
├── resources
└── js
│ ├── components
│ └── DynamicActionFieldModal.vue
│ └── dynamic-action-fields.js
├── routes
└── api.php
├── screenshot.png
├── src
├── DynamicFieldAction.php
├── DynamicFieldsController.php
└── DynamicFieldsServiceProvider.php
└── webpack.mix.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /vendor
3 | /node_modules
4 | package-lock.json
5 | composer.phar
6 | composer.lock
7 | phpunit.xml
8 | .phpunit.result.cache
9 | .DS_Store
10 | Thumbs.db
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Tunezilla Software Ltd
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dynamic Action Fields for Laravel Nova
2 |
3 | This package allows you to change the fields of a Laravel Nova Action depending on which resources are selected.
4 |
5 | **This is experimental, proceed at your own risk**
6 |
7 | ## Screenshot
8 |
9 | 
10 |
11 | ## Installation
12 |
13 | ```
14 | composer require tunezilla/dynamic-action-fields
15 | ```
16 |
17 | ## Usage
18 |
19 | 1. Add the `DynamicFieldAction` trait to your resource
20 |
21 | 2. Change your `public function fields()` to `public function fieldsForModels(Collection $models): array`
22 |
23 | Example:
24 |
25 | ```php
26 | isEmpty()) {
52 | return [];
53 | }
54 |
55 | /** @var User $user */
56 | $user = $models->first();
57 |
58 | return [
59 | Text::make('Email')
60 | ->help('The new email for ' . $user->name),
61 | $user->hasVerifiedEmail()
62 | ? Boolean::make('Require Verification')
63 | ->help('Send a verification email that they must accept')
64 | : Hidden::make('Require Verification'),
65 | ];
66 | }
67 |
68 | public function handle(ActionFields $fields, Collection $models)
69 | {
70 | /** @var User $user */
71 | $user = $models->first();
72 | $user->email = $fields->get('email');
73 |
74 | if ($fields->get('require_verification')) {
75 | $user->email_verified_at = null;
76 | }
77 |
78 | $user->save();
79 |
80 | if (!$user->hasVerifiedEmail()) {
81 | $user->sendEmailVerificationNotification();
82 | }
83 |
84 | return Action::message(implode(' ', [
85 | 'Changed email to ' . $fields->get('email'),
86 | $fields->get('require_verification')
87 | ? 'and sent a verification email'
88 | : 'but did not send a verification email'
89 | ]));
90 | }
91 | }
92 | ```
93 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tunezilla/dynamic-action-fields",
3 | "description": "Dynamic Action Fields for Laravel Nova",
4 | "keywords": [
5 | "laravel",
6 | "nova"
7 | ],
8 | "license": "MIT",
9 | "require": {
10 | "php": ">=7.1.0"
11 | },
12 | "autoload": {
13 | "psr-4": {
14 | "TuneZilla\\DynamicActionFields\\": "src/"
15 | }
16 | },
17 | "extra": {
18 | "laravel": {
19 | "providers": [
20 | "TuneZilla\\DynamicActionFields\\DynamicFieldsServiceProvider"
21 | ]
22 | }
23 | },
24 | "config": {
25 | "sort-packages": true
26 | },
27 | "minimum-stability": "dev",
28 | "prefer-stable": true
29 | }
30 |
--------------------------------------------------------------------------------
/dist/js/dynamic-action-fields.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:o})},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){Nova.booting(function(e,t,o){e.component("dynamic-action-field-modal",n(2))})},function(e,t,n){var o=n(3)(n(4),n(5),!1,null,null,null);e.exports=o.exports},function(e,t){e.exports=function(e,t,n,o,r,i){var c,s=e=e||{},a=typeof e.default;"object"!==a&&"function"!==a||(c=e,s=e.default);var u,d="function"==typeof s?s.options:s;if(t&&(d.render=t.render,d.staticRenderFns=t.staticRenderFns,d._compiled=!0),n&&(d.functional=!0),r&&(d._scopeId=r),i?(u=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__),o&&o.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(i)},d._ssrRegister=u):o&&(u=o),u){var l=d.functional,f=l?d.render:d.beforeCreate;l?(d._injectStyles=u,d.render=function(e,t){return u.call(t),f(e,t)}):d.beforeCreate=f?[].concat(f,u):[u]}return{esModule:c,exports:s,options:d}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"dynamic-action-field-modal",props:{working:Boolean,resourceName:{type:String,required:!0},action:{type:Object,required:!0},selectedResources:{type:[Array,String],required:!0},errors:{type:Object,required:!0}},data:function(){return{dynamicAction:null}},methods:{loadAction:function(){var e=this;this.dynamicAction=null,window.axios.get("/nova-vendor/dynamic-action-fields/actions/"+this.resourceName+"/dynamic-fields",{params:{resources:Array.isArray(this.selectedResources)?this.selectedResources.join(","):this.selectedResources,action:this.action.uriKey}}).then(function(t){e.dynamicAction=t.data,e.action.fields=e.dynamicAction.fields})}},mounted:function(){this.loadAction()},watch:{action:function(){this.loadAction()},resourceName:function(){this.loadAction()},selectedResources:function(){this.loadAction()}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return e.dynamicAction?n("confirm-action-modal",e._b({attrs:{action:e.dynamicAction},on:{confirm:function(t){return e.$emit("confirm")},close:function(t){return e.$emit("close")}}},"confirm-action-modal",{working:e.working,resourceName:e.resourceName,selectedResources:e.selectedResources,errors:e.errors},!1)):n("modal",{attrs:{tabindex:"-1",role:"dialog"},nativeOn:{click:function(t){return e.$emit("close")}}},[n("div",{staticClass:"flex items-center justify-center z-50 p-6",staticStyle:{"min-height":"150px"}},[n("loader",{staticClass:"text-60"})],1)])},staticRenderFns:[]}}]);
--------------------------------------------------------------------------------
/dist/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/js/dynamic-action-fields.js": "/js/dynamic-action-fields.js"
3 | }
--------------------------------------------------------------------------------
/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 | },
12 | "devDependencies": {
13 | "cross-env": "^5.0.0",
14 | "laravel-mix": "^1.0"
15 | },
16 | "dependencies": {
17 | "vue": "^2.5.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/resources/js/components/DynamicActionFieldModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
74 |
--------------------------------------------------------------------------------
/resources/js/dynamic-action-fields.js:
--------------------------------------------------------------------------------
1 | Nova.booting((Vue, router, store) => {
2 | Vue.component('dynamic-action-field-modal', require('./components/DynamicActionFieldModal.vue'))
3 | })
4 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | actionRequest ?? app(ActionRequest::class);
22 |
23 | $models = [];
24 |
25 | if ($actionRequest->has('resources')) {
26 | $actionRequest->chunks(100, function ($chunkedModels) use (&$models) {
27 | /** @var \Illuminate\Database\Eloquent\Collection $chunkedModels */
28 | $models = array_merge($models, $chunkedModels->all());
29 | });
30 | }
31 |
32 | return $this->fieldsForModels(collect($models));
33 | }
34 |
35 | abstract public function fieldsForModels(Collection $models): array;
36 | }
37 |
--------------------------------------------------------------------------------
/src/DynamicFieldsController.php:
--------------------------------------------------------------------------------
1 | json($request->action());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/DynamicFieldsServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->booted(function () {
20 | $this->routes();
21 | });
22 |
23 | Nova::serving(function (ServingNova $event) {
24 | Nova::script('dynamic-action-fields', __DIR__.'/../dist/js/dynamic-action-fields.js');
25 | });
26 | }
27 |
28 | /**
29 | * Register the card's routes.
30 | *
31 | * @return void
32 | */
33 | protected function routes()
34 | {
35 | if ($this->app->routesAreCached()) {
36 | return;
37 | }
38 |
39 | Route::middleware(['nova'])
40 | ->prefix('nova-vendor/dynamic-action-fields')
41 | ->group(__DIR__.'/../routes/api.php');
42 | }
43 |
44 | /**
45 | * Register any application services.
46 | *
47 | * @return void
48 | */
49 | public function register()
50 | {
51 | //
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | let mix = require('laravel-mix')
2 |
3 | mix
4 | .setPublicPath('dist')
5 | .js('resources/js/dynamic-action-fields.js', 'js')
6 |
--------------------------------------------------------------------------------