[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\n\n[*.php]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true"
  },
  {
    "path": ".gitattributes",
    "content": "# Path-based git attributes\n# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html\n\n# Ignore all test and documentation with \"export-ignore\".\n/.gitattributes     export-ignore\n/.gitignore         export-ignore\n/.travis.yml        export-ignore\n/phpunit.xml.dist   export-ignore\n/.scrutinizer.yml   export-ignore\n/.styleci.yml       export-ignore\n/tests              export-ignore\n/.editorconfig      export-ignore\n/docs               export-ignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: outl1ne\n"
  },
  {
    "path": ".gitignore",
    "content": "/.idea\n/vendor\n/node_modules\ncomposer.phar\ncomposer.lock\nphpunit.xml\n.phpunit.result.cache\n.DS_Store\nThumbs.db\n.env.dusk\ntests/Browser/console\ntests/Browser/screenshots\nauth.json\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"printWidth\": 120,\n  \"singleQuote\": true,\n  \"trailingComma\": \"es5\",\n  \"arrowParens\": \"avoid\"\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [6.0.2] - 01-09-2025\n\n### Added\n\n- Added support for Markdown field preview\n\n## [6.0.0] - 18-12-2024\n\n### Added\n\n- Nova 5 initial support\n\n## [5.3.0] - 18-12-2024\n\n### Added\n\n- Added hugely improved caching store with options (thanks to [@manuel-watchenterprise](https://github.com/manuel-watchenterprise))\n\n## [5.2.4] - 03-02-2024\n\n### Added\n\n- Added Spanish localization (thanks to [@dualklip](https://github.com/dualklip))\n\n## [5.2.3] - 09-10-2023\n\n### Fixed\n\n- Fixed save button missing in Nova 4.28 (thanks to [@alancolant](https://github.com/alancolant))\n\n## [5.2.2] - 09-10-2023\n\n### Fixed\n\n- Fixed casting of date and datetime objects when passing them into field ([see issue](https://github.com/outl1ne/nova-settings/issues/172))\n\n## [5.2.1] - 10-08-2023\n\n### Added\n\n- Added Nova ->domain() support to routes (thanks to [@RonMelkhior](https://github.com/RonMelkhior))\n\n### Changed\n\n- Fixed null values not being persisted (thanks to [@Senexis](https://github.com/Senexis))\n\n## [5.2.0] - 29-06-2023\n\n### Added\n\n- Added Nova 4.26 support (thanks to [@puzzledmonkey](https://github.com/puzzledmonkey))\n\n## [5.1.0] - 20-03-2023\n\n### Added\n\n- Added Slovak language (thanks to [@wamesro](https://github.com/wamesro))\n- Added resource-loaded and resource-updated Nova events\n\n### Changed\n\n- Allow encoding of JsonSerializable objects (thanks to [@miagg](https://github.com/miagg))\n- Settings submenu is now hidden if there is only 1 menu element (thanks to [@johnpuddephatt](https://github.com/johnpuddephatt))\n- Fixed image deletion when the image is inside a \\Nova\\Panel or \\Eminiarts\\Tabs (thanks to [@marttinnotta](https://github.com/marttinnotta))\n- Updated packages\n\n## [5.0.8] - 04-01-2023\n\n### Changed\n\n- Fixed `nova_get_settings()` not casting as expected\n\n## [5.0.7] - 04-01-2023\n\n### Added\n\n- Added dusk identifier to update button (thanks to [@chrillep](https://github.com/chrillep))\n\n### Changed\n\n- Fixed `nova_get_settings()` not working as expected with default values\n- Updated packages\n\n## [5.0.6] - 21-10-2022\n\n### Changed\n\n- Added translations for French language (thanks to [@shaffe-fr](https://github.com/shaffe-fr))\n\n## [5.0.5] - 08-09-2022\n\n### Changed\n\n- Fixed help text not rendering (thanks to [@mberatsanli](https://github.com/mberatsanli))\n\n## [5.0.4] - 19-08-2022\n\n### Changed\n\n- Fixed nova-tabs support (thanks to [@Gertiozuni](https://github.com/Gertiozuni))\n- Updated packages\n\n## [5.0.3] - 19-07-2022\n\n### Changed\n\n- Fixed File and Image fields not deleting files from disk\n\n## [5.0.2] - 24-05-2022\n\n### Added\n\n- Added Turkish translations (thanks to [@suleymanozev](https://github.com/suleymanozev))\n\n### Changed\n\n- Fixed not being redirected to login when accessing settings while unauthenticated (thanks to [@ianrobertsFF](https://github.com/ianrobertsFF))\n\n## [5.0.1] - 14-05-2022\n\n### Changed\n\n- Fixed migrations (thanks to [@AndreasFurster](https://github.com/AndreasFurster))\n\n## [5.0.0] - 13-05-2022\n\n### Changed\n\n- NB! Changed namespace from OptimistDigital to Outl1ne\n- Allow redirections as a result of settings updates (thanks to [@ianrobertsFF](https://github.com/ianrobertsFF))\n- Fixed sidebar subpages titles (thanks to [@faab007nl](https://github.com/faab007nl))\n- Updated packages\n\n## [4.0.4] - 29-04-2022\n\n### Changed\n\n- Removed loadViewsFrom() call from ServiceProvider\n- Fixed memory cache not clearing after settings update\n- Updated packages\n\n## [4.0.3] - 25-04-2022\n\n### Changed\n\n- Changed `empty` check to `isset` when loading settings to allow negative but defined values\n\n## [4.0.2] - 08-04-2022\n\n### Changed\n\n- Reworked routing logic\n\n## [4.0.1] - 08-04-2022\n\n### Changed\n\n- Fixed page titles\n\n## [4.0.0] - 08-04-2022\n\n### Added\n\n- Nova 4 support\n- Fully compatible with light and dark modes\n\n### Changed\n\n- Dropped Laravel 7 and 8 support\n- Dropped PHP 7.X support\n- Dropped Nova 3 support\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2019 Outl1ne <info@optimistdigital.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Nova Settings\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/outl1ne/nova-settings.svg?style=flat-square)](https://packagist.org/packages/outl1ne/nova-settings)\n[![Total Downloads](https://img.shields.io/packagist/dt/outl1ne/nova-settings.svg?style=flat-square)](https://packagist.org/packages/outl1ne/nova-settings)\n\nThis [Laravel Nova](https://nova.laravel.com) package allows you to create custom settings in code (using Nova's native fields) and creates a UI for the users where the settings can be edited.\n\n## Requirements\n\n- `php: >=8.0`\n- `laravel/nova: ^4.26`\n\n## Features\n\n- Settings fields management in code\n- UI for editing settings\n- Helpers for accessing settings\n- Rule validation support\n\n## Screenshots\n\n![Settings View](docs/index.png)\n\n## Installation\n\nInstall the package in a Laravel Nova project via Composer and run migrations:\n\n```bash\n# Install nova-settings\ncomposer require outl1ne/nova-settings\n\n# Run migrations\nphp artisan migrate\n```\n\nRegister the tool with Nova in the `tools()` method of the `NovaServiceProvider`:\n\n```php\n// in app/Providers/NovaServiceProvider.php\n\npublic function tools()\n{\n    return [\n        // ...\n        new \\Outl1ne\\NovaSettings\\NovaSettings\n    ];\n}\n```\n\n## Usage\n\n### Registering fields\n\nDefine the fields in your `NovaServiceProvider`'s `boot()` function by calling `NovaSettings::addSettingsFields()`.\n\n```php\n// Using an array\n\\Outl1ne\\NovaSettings\\NovaSettings::addSettingsFields([\n    Text::make('Some setting', 'some_setting'),\n    Number::make('A number', 'a_number'),\n]);\n\n// OR\n\n// Using a callable\n\\Outl1ne\\NovaSettings\\NovaSettings::addSettingsFields(function() {\n  return [\n    Text::make('Some setting', 'some_setting'),\n    Number::make('A number', 'a_number'),\n  ];\n});\n```\n\n#### Registering field panels\n\n```php\n// Using an array\n\\Outl1ne\\NovaSettings\\NovaSettings::addSettingsFields([\n    Panel::make('Panel Title', [\n      Text::make('Some setting', 'some_setting'),\n      Number::make('A number', 'a_number'),\n    ]),\n]);\n```\n\n### Casts\n\nIf you want the value of the setting to be formatted before it's returned, pass an array similar to `Eloquent`'s `$casts` property as the second parameter.\n\n```php\n\\Outl1ne\\NovaSettings\\NovaSettings::addSettingsFields([\n    // ... fields\n], [\n  'some_boolean_value' => 'boolean',\n  'some_float' => 'float',\n  'some_collection' => 'collection',\n  // ...\n]);\n```\n\n### Subpages\n\nAdd a settings page name as a third argument to list those settings in a custom subpage.\n\n```php\n\\Outl1ne\\NovaSettings\\NovaSettings::addSettingsFields([\n    Text::make('Some setting', 'some_setting'),\n    Number::make('A number', 'a_number'),\n], [], 'Subpage');\n```\n\nIf you leave the custom name empty, the field(s) will be listed under \"General\".\n\nTo translate the page name, publish the translations and add a new key `novaSettings.$subpage` to the respective translations file, where `$subpage` is the name of the page (full lowercase, slugified).\n\n### Authorization\n\n#### Show/hide all settings\n\nIf you want to hide the whole `Settings` area from the sidebar, you can authorize the `NovaSettings` tool like so:\n\n```php\npublic function tools(): array\n{\n    return [\n        NovaSettings::make()->canSee(fn () => user()->isAdmin()),\n    ];\n}\n```\n\n#### Show/hide specific setting fields\n\nIf you want to hide only some settings, you can use `->canSee(fn () => ...)` per field. Like so:\n\n```php\nText::make('A text field')\n  ->canSee(fn () => user()->isAdmin()),\n```\n\n### Helper functions\n\n#### nova_get_settings(\\$keys = null, \\$defaults = [])\n\nCall `nova_get_settings()` to get all the settings formated as a regular array. Additionally, you can pass a `key => value` array as a second argument: `nova_get_settings(['some_key], ['some_key' => 'default_value'])`.\n\n#### nova_get_setting(\\$key, \\$default = null)\n\nTo get a single setting's value, call `nova_get_setting('some_setting_key')`. It will return either a value or null if there's no setting with such key.\n\nYou can also pass default value as a second argument `nova_get_setting('some_setting_key', 'default_value')`, which will be returned, if no setting was found with given key.\n\n#### nova_set_setting_value(\\$key, \\$value = null)\n\nSets a setting value for the given key.\n\n## Configuration\n\nThe config file can be published using the following command:\n\n```bash\nphp artisan vendor:publish --provider=\"Outl1ne\\NovaSettings\\NovaSettingsServiceProvider\" --tag=\"config\"\n```\n\n| Name                  | Type    | Default           | Description                                                                                      |\n|-----------------------|---------|-------------------|--------------------------------------------------------------------------------------------------|\n| `base_path`           | String  | `nova-settings`   | URL path of settings page.                                                                       |\n| `reload_page_on_save` | Boolean | false             | Reload the entire page on save. Useful when updating any Nova UI related settings.               |\n| `models.settings`     | Model   | `Settings::class` | Optionally override the Settings model.                                                          |\n| `cache`               | String  | `:memory:`        | Cache store name to use that cache, \":memory:\" for singleton class, or null to turn off caching. |\n\nThe migration can also be published and overwritten using:\n\n```bash\nphp artisan vendor:publish --provider=\"Outl1ne\\NovaSettings\\NovaSettingsServiceProvider\" --tag=\"migrations\"\n```\n\n## Localization\n\nThe translation file(s) can be published by using the following command:\n\n```bash\nphp artisan vendor:publish --provider=\"Outl1ne\\NovaSettings\\NovaSettingsServiceProvider\" --tag=\"translations\"\n```\n\nYou can add your translations to `resources/lang/vendor/nova-settings/` by creating a new translations file with the locale name (ie `et.json`) and copying the JSON from the existing `en.json`.\n\n## Credits\n\n- [Tarvo Reinpalu](https://github.com/Tarpsvo)\n\n## License\n\nNova Settings is open-sourced software licensed under the [MIT license](LICENSE.md).\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"outl1ne/nova-settings\",\n  \"description\": \"A Laravel Nova tool for editing custom settings using native Nova fields.\",\n  \"keywords\": [\n    \"laravel\",\n    \"nova\",\n    \"settings\"\n  ],\n  \"authors\": [\n    {\n      \"name\": \"Tarvo Reinpalu\",\n      \"email\": \"tarvo@outl1ne.com\",\n      \"role\": \"Developer\"\n    },\n    {\n      \"name\": \"Outl1ne\",\n      \"email\": \"info@outl1ne.com\",\n      \"role\": \"Maintainer\"\n    }\n  ],\n  \"license\": \"MIT\",\n  \"require\": {\n    \"php\": \">=8.1\",\n    \"laravel/nova\": \"^5.0\",\n    \"outl1ne/nova-translations-loader\": \"^5.0\"\n  },\n  \"require-dev\": {\n    \"laravel/nova-devtool\": \"^1.0\",\n    \"nunomaduro/collision\": \"^7.8\",\n    \"orchestra/testbench\": \"^8.30|^9.8\"\n  },\n  \"autoload\": {\n    \"psr-4\": {\n      \"Outl1ne\\\\NovaSettings\\\\\": \"src/\"\n    },\n    \"files\": [\n      \"./src/helpers.php\"\n    ]\n  },\n  \"autoload-dev\": {\n    \"psr-4\": {\n      \"Outl1ne\\\\NovaSettings\\\\Tests\\\\\": \"tests\"\n    }\n  },\n  \"extra\": {\n    \"laravel\": {\n      \"providers\": [\n        \"Outl1ne\\\\NovaSettings\\\\NovaSettingsServiceProvider\"\n      ]\n    }\n  },\n  \"config\": {\n    \"sort-packages\": true,\n    \"allow-plugins\": {\n      \"php-http/discovery\": true\n    }\n  },\n  \"minimum-stability\": \"dev\",\n  \"prefer-stable\": true,\n  \"repositories\": [\n    {\n      \"type\": \"composer\",\n      \"url\": \"https://nova.laravel.com\"\n    }\n  ],\n  \"scripts\": {\n    \"dusk:prepare\": [\n      \"./vendor/bin/dusk-updater detect --auto-update\"\n    ],\n    \"dusk:assets\": [\n      \"npm ci\",\n      \"npm run prod\",\n      \"./vendor/bin/testbench-dusk nova:publish\"\n    ],\n    \"dusk:test\": [\n      \"./vendor/bin/phpunit -c phpunit.dusk.xml.dist\"\n    ]\n  }\n}\n"
  },
  {
    "path": "config/nova-settings.php",
    "content": "<?php\n\nreturn [\n\n    /**\n     * Set a name for the settings table\n     */\n    'table' => 'nova_settings',\n\n    /**\n     * URL path of settings page\n     */\n    'base_path' => 'nova-settings',\n\n    /**\n     * Reload the entire page on save. Useful when updating any Nova UI related settings.\n     */\n    'reload_page_on_save' => false,\n\n    /**\n     * We need to know which eloquent model should be used to retrieve your permissions.\n     * Of course, it is often just the default model but you may use whatever you like.\n     *\n     * The model you want to use as a model needs to extend the original model.\n     */\n    'models' => [\n        'settings' => \\Outl1ne\\NovaSettings\\Models\\Settings::class,\n    ],\n\n    /**\n     * Show the sidebar menu\n     */\n    'show_in_sidebar' => true,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Cache settings\n    |--------------------------------------------------------------------------\n    |\n    | Here you may specify which of the cache connection should be used to\n    | cache the settings. `:memory:` is the default which is a simple\n    | in-memory cache through a singleton service class property.\n    | `null` will disable caching.\n    |\n    */\n    'cache' => [\n        'store' => env('NOVA_SETTINGS_CACHE_DRIVER', ':memory:'),\n\n        'prefix' => 'nova-settings:',\n    ],\n];\n"
  },
  {
    "path": "database/migrations/2019_08_13_000000_create_nova_settings_table.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Outl1ne\\NovaSettings\\NovaSettings;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     *\n     * @return void\n     */\n    public function up()\n    {\n        // Settings table\n        Schema::create(NovaSettings::getSettingsTableName(), function (Blueprint $table) {\n            $table->string('key')->unique()->primary();\n            $table->text('value')->nullable();\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     *\n     * @return void\n     */\n    public function down()\n    {\n        Schema::dropIfExists(NovaSettings::getSettingsTableName());\n    }\n};\n"
  },
  {
    "path": "database/migrations/2021_02_15_000000_update_nova_settings_value_column.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Outl1ne\\NovaSettings\\NovaSettings;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     *\n     * @return void\n     */\n    public function up()\n    {\n        // TODO Remove in next major release\n        // Legacy support\n        // Convert value column to text if needed as the 'value' column was previously a varchar\n        Schema::table(NovaSettings::getSettingsTableName(), function (Blueprint $table) {\n            $table->text('value')->nullable()->change();\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     *\n     * @return void\n     */\n    public function down()\n    {\n        // No down because previous migration was also modified\n    }\n};\n"
  },
  {
    "path": "dist/js/entry.js",
    "content": "/*! For license information please see entry.js.LICENSE.txt */\n(()=>{var t={262:(t,e)=>{\"use strict\";e.A=(t,e)=>{const r=t.__vccOpts||t;for(const[t,n]of e)r[t]=n;return r}},189:(t,e,r)=>{\"use strict\";r.d(e,{A:()=>v});const n=Vue;var o={key:0,class:\"flex items-center\"},a={key:1,class:\"bg-white dark:bg-gray-800 rounded-lg shadow p-3\"},i={class:\"flex flex-col justify-center align-center\"},c={class:\"w-3/4 py-4 text-center\"},s={class:\"text-90\"};const u=LaravelNova;function l(t){return l=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},l(t)}function f(){f=function(){return e};var t,e={},r=Object.prototype,n=r.hasOwnProperty,o=Object.defineProperty||function(t,e,r){t[e]=r.value},a=\"function\"==typeof Symbol?Symbol:{},i=a.iterator||\"@@iterator\",c=a.asyncIterator||\"@@asyncIterator\",s=a.toStringTag||\"@@toStringTag\";function u(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{u({},\"\")}catch(t){u=function(t,e,r){return t[e]=r}}function p(t,e,r,n){var a=e&&e.prototype instanceof w?e:w,i=Object.create(a.prototype),c=new I(n||[]);return o(i,\"_invoke\",{value:O(t,r,c)}),i}function d(t,e,r){try{return{type:\"normal\",arg:t.call(e,r)}}catch(t){return{type:\"throw\",arg:t}}}e.wrap=p;var h=\"suspendedStart\",v=\"suspendedYield\",g=\"executing\",y=\"completed\",m={};function w(){}function b(){}function x(){}var E={};u(E,i,(function(){return this}));var k=Object.getPrototypeOf,L=k&&k(k(F([])));L&&L!==r&&n.call(L,i)&&(E=L);var _=x.prototype=w.prototype=Object.create(E);function N(t){[\"next\",\"throw\",\"return\"].forEach((function(e){u(t,e,(function(t){return this._invoke(e,t)}))}))}function S(t,e){function r(o,a,i,c){var s=d(t[o],t,a);if(\"throw\"!==s.type){var u=s.arg,f=u.value;return f&&\"object\"==l(f)&&n.call(f,\"__await\")?e.resolve(f.__await).then((function(t){r(\"next\",t,i,c)}),(function(t){r(\"throw\",t,i,c)})):e.resolve(f).then((function(t){u.value=t,i(u)}),(function(t){return r(\"throw\",t,i,c)}))}c(s.arg)}var a;o(this,\"_invoke\",{value:function(t,n){function o(){return new e((function(e,o){r(t,n,e,o)}))}return a=a?a.then(o,o):o()}})}function O(e,r,n){var o=h;return function(a,i){if(o===g)throw Error(\"Generator is already running\");if(o===y){if(\"throw\"===a)throw i;return{value:t,done:!0}}for(n.method=a,n.arg=i;;){var c=n.delegate;if(c){var s=T(c,n);if(s){if(s===m)continue;return s}}if(\"next\"===n.method)n.sent=n._sent=n.arg;else if(\"throw\"===n.method){if(o===h)throw o=y,n.arg;n.dispatchException(n.arg)}else\"return\"===n.method&&n.abrupt(\"return\",n.arg);o=g;var u=d(e,r,n);if(\"normal\"===u.type){if(o=n.done?y:v,u.arg===m)continue;return{value:u.arg,done:n.done}}\"throw\"===u.type&&(o=y,n.method=\"throw\",n.arg=u.arg)}}}function T(e,r){var n=r.method,o=e.iterator[n];if(o===t)return r.delegate=null,\"throw\"===n&&e.iterator.return&&(r.method=\"return\",r.arg=t,T(e,r),\"throw\"===r.method)||\"return\"!==n&&(r.method=\"throw\",r.arg=new TypeError(\"The iterator does not provide a '\"+n+\"' method\")),m;var a=d(o,e.iterator,r.arg);if(\"throw\"===a.type)return r.method=\"throw\",r.arg=a.arg,r.delegate=null,m;var i=a.arg;return i?i.done?(r[e.resultName]=i.value,r.next=e.nextLoc,\"return\"!==r.method&&(r.method=\"next\",r.arg=t),r.delegate=null,m):i:(r.method=\"throw\",r.arg=new TypeError(\"iterator result is not an object\"),r.delegate=null,m)}function j(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function B(t){var e=t.completion||{};e.type=\"normal\",delete e.arg,t.completion=e}function I(t){this.tryEntries=[{tryLoc:\"root\"}],t.forEach(j,this),this.reset(!0)}function F(e){if(e||\"\"===e){var r=e[i];if(r)return r.call(e);if(\"function\"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,a=function r(){for(;++o<e.length;)if(n.call(e,o))return r.value=e[o],r.done=!1,r;return r.value=t,r.done=!0,r};return a.next=a}}throw new TypeError(l(e)+\" is not iterable\")}return b.prototype=x,o(_,\"constructor\",{value:x,configurable:!0}),o(x,\"constructor\",{value:b,configurable:!0}),b.displayName=u(x,s,\"GeneratorFunction\"),e.isGeneratorFunction=function(t){var e=\"function\"==typeof t&&t.constructor;return!!e&&(e===b||\"GeneratorFunction\"===(e.displayName||e.name))},e.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,x):(t.__proto__=x,u(t,s,\"GeneratorFunction\")),t.prototype=Object.create(_),t},e.awrap=function(t){return{__await:t}},N(S.prototype),u(S.prototype,c,(function(){return this})),e.AsyncIterator=S,e.async=function(t,r,n,o,a){void 0===a&&(a=Promise);var i=new S(p(t,r,n,o),a);return e.isGeneratorFunction(r)?i:i.next().then((function(t){return t.done?t.value:i.next()}))},N(_),u(_,s,\"Generator\"),u(_,i,(function(){return this})),u(_,\"toString\",(function(){return\"[object Generator]\"})),e.keys=function(t){var e=Object(t),r=[];for(var n in e)r.push(n);return r.reverse(),function t(){for(;r.length;){var n=r.pop();if(n in e)return t.value=n,t.done=!1,t}return t.done=!0,t}},e.values=F,I.prototype={constructor:I,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=t,this.done=!1,this.delegate=null,this.method=\"next\",this.arg=t,this.tryEntries.forEach(B),!e)for(var r in this)\"t\"===r.charAt(0)&&n.call(this,r)&&!isNaN(+r.slice(1))&&(this[r]=t)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if(\"throw\"===t.type)throw t.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var r=this;function o(n,o){return c.type=\"throw\",c.arg=e,r.next=n,o&&(r.method=\"next\",r.arg=t),!!o}for(var a=this.tryEntries.length-1;a>=0;--a){var i=this.tryEntries[a],c=i.completion;if(\"root\"===i.tryLoc)return o(\"end\");if(i.tryLoc<=this.prev){var s=n.call(i,\"catchLoc\"),u=n.call(i,\"finallyLoc\");if(s&&u){if(this.prev<i.catchLoc)return o(i.catchLoc,!0);if(this.prev<i.finallyLoc)return o(i.finallyLoc)}else if(s){if(this.prev<i.catchLoc)return o(i.catchLoc,!0)}else{if(!u)throw Error(\"try statement without catch or finally\");if(this.prev<i.finallyLoc)return o(i.finallyLoc)}}}},abrupt:function(t,e){for(var r=this.tryEntries.length-1;r>=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,\"finallyLoc\")&&this.prev<o.finallyLoc){var a=o;break}}a&&(\"break\"===t||\"continue\"===t)&&a.tryLoc<=e&&e<=a.finallyLoc&&(a=null);var i=a?a.completion:{};return i.type=t,i.arg=e,a?(this.method=\"next\",this.next=a.finallyLoc,m):this.complete(i)},complete:function(t,e){if(\"throw\"===t.type)throw t.arg;return\"break\"===t.type||\"continue\"===t.type?this.next=t.arg:\"return\"===t.type?(this.rval=this.arg=t.arg,this.method=\"return\",this.next=\"end\"):\"normal\"===t.type&&e&&(this.next=e),m},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),B(r),m}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if(\"throw\"===n.type){var o=n.arg;B(r)}return o}}throw Error(\"illegal catch attempt\")},delegateYield:function(e,r,n){return this.delegate={iterator:F(e),resultName:r,nextLoc:n},\"next\"===this.method&&(this.arg=t),m}},e}function p(t,e,r,n,o,a,i){try{var c=t[a](i),s=c.value}catch(t){return void r(t)}c.done?e(s):Promise.resolve(s).then(n,o)}function d(t){return function(){var e=this,r=arguments;return new Promise((function(n,o){var a=t.apply(e,r);function i(t){p(a,n,o,i,c,\"next\",t)}function c(t){p(a,n,o,i,c,\"throw\",t)}i(void 0)}))}}const h={components:{Button:LaravelNovaUi.Button},data:function(){return{pageId:!1,loading:!1,isUpdating:!1,fields:[],panels:[],authorizations:[],validationErrors:new u.Errors}},created:function(){var t=this;return d(f().mark((function e(){return f().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.pageId=t.$page.props.pageId||\"general\",t.getFields();case 2:case\"end\":return e.stop()}}),e)})))()},methods:{getFields:function(){var t=this;return d(f().mark((function e(){var r,n,o,a,i,c,s;return f().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t.loading=!0,t.fields=[],r={editing:!0,editMode:\"update\"},t.pageId&&(r.path=t.pageId),e.next=6,Nova.request().get(\"/nova-vendor/nova-settings/settings\",{params:r}).catch((function(t){t.response.status}));case 6:n=e.sent,o=n.data,a=o.fields,i=o.panels,c=o.authorizations,t.fields=a,t.panels=i,t.authorizations=c,t.loading=!1,s=t.isUpdating?\"resource-updated\":\"resource-loaded\",Nova.$emit(s,{resourceName:\"nova-settings\"});case 17:case\"end\":return e.stop()}}),e)})))()},update:function(){var t=this;return d(f().mark((function e(){var r;return f().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,t.isUpdating=!0,e.next=4,t.updateRequest();case 4:if(!(r=e.sent)||!r.data){e.next=14;break}if(!0!==r.data.reload){e.next=11;break}return location.reload(),e.abrupt(\"return\");case 11:if(!(r.data.redirect&&r.data.redirect.length>0)){e.next=14;break}return location.replace(r.data.redirect),e.abrupt(\"return\");case 14:return Nova.success(t.__(\"novaSettings.settingsSuccessToast\")),e.next=17,t.getFields();case 17:t.isUpdating=!1,t.validationErrors=new u.Errors,e.next=26;break;case 21:e.prev=21,e.t0=e.catch(0),console.error(e.t0),t.isUpdating=!1,e.t0&&e.t0.response&&422==e.t0.response.status&&(t.validationErrors=new u.Errors(e.t0.response.data.errors),Nova.error(t.__(\"There was a problem submitting the form.\")));case 26:case\"end\":return e.stop()}}),e,null,[[0,21]])})))()},updateRequest:function(){return Nova.request().post(\"/nova-vendor/nova-settings/settings\",this.formData)}},computed:{formData:function(){var t=new FormData;return this.fields.forEach((function(e){return e.fill(t)})),t.append(\"_method\",\"POST\"),this.pageId&&t.append(\"path\",this.pageId),t},panelsWithFields:function(){var t=this;return this.panels.map((function(e){return{name:e.name,component:e.component,helpText:e.helpText,fields:t.fields.filter((function(t){return t.panel===e.name})),showTitle:e.showTitle}}))}}};const v=(0,r(262).A)(h,[[\"render\",function(t,e,r,u,l,f){var p=(0,n.resolveComponent)(\"Head\"),d=(0,n.resolveComponent)(\"Button\"),h=(0,n.resolveComponent)(\"LoadingView\");return(0,n.openBlock)(),(0,n.createBlock)(h,{loading:l.loading,key:l.pageId},{default:(0,n.withCtx)((function(){return[(0,n.createVNode)(p,{title:t.__(\"novaSettings.navigationItemTitle\")+(\"general\"!==l.pageId?\" (\".concat(l.pageId,\")\"):\"\")},null,8,[\"title\"]),l.fields&&l.fields.length?((0,n.openBlock)(),(0,n.createElementBlock)(\"form\",{key:0,onSubmit:e[0]||(e[0]=(0,n.withModifiers)((function(){return f.update&&f.update.apply(f,arguments)}),[\"prevent\"])),autocomplete:\"off\",dusk:\"nova-settings-form\"},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(f.panelsWithFields,(function(t){return(0,n.openBlock)(),(0,n.createBlock)((0,n.resolveDynamicComponent)(\"form-\"+t.component),{key:t.name,panel:t,name:t.name,fields:t.fields,\"resource-name\":\"nova-settings\",\"resource-id\":l.pageId,mode:\"form\",class:\"mb-6\",\"validation-errors\":l.validationErrors,\"show-help-text\":!0},null,8,[\"panel\",\"name\",\"fields\",\"resource-id\",\"validation-errors\"])})),128)),l.authorizations.authorizedToUpdate?((0,n.openBlock)(),(0,n.createElementBlock)(\"div\",o,[(0,n.createVNode)(d,{dusk:\"update-button\",type:\"submit\",class:\"ml-auto\",disabled:l.isUpdating,loading:l.isUpdating},{default:(0,n.withCtx)((function(){return[(0,n.createTextVNode)((0,n.toDisplayString)(t.__(\"novaSettings.saveButtonText\")),1)]})),_:1},8,[\"disabled\",\"loading\"])])):(0,n.createCommentVNode)(\"\",!0)],32)):((0,n.openBlock)(),(0,n.createElementBlock)(\"div\",a,[(0,n.createElementVNode)(\"div\",i,[(0,n.createElementVNode)(\"div\",c,[(0,n.createElementVNode)(\"p\",s,(0,n.toDisplayString)(t.__(\"novaSettings.noSettingsFieldsText\")),1)])])]))]})),_:1},8,[\"loading\"])}]])}},e={};function r(n){var o=e[n];if(void 0!==o)return o.exports;var a=e[n]={exports:{}};return t[n](a,a.exports,r),a.exports}r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Nova.booting((function(t,e,n){Nova.inertia(\"NovaSettings\",r(189).A)}))})();"
  },
  {
    "path": "dist/js/entry.js.LICENSE.txt",
    "content": "/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */\n"
  },
  {
    "path": "dist/mix-manifest.json",
    "content": "{\n    \"/js/entry.js\": \"/js/entry.js\"\n}\n"
  },
  {
    "path": "lang/ar.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"الإعدادات\",\n  \"novaSettings.saveButtonText\": \"حفظ الإعدادات\",\n  \"novaSettings.noSettingsFieldsText\": \"لم يتم اضافة حقول للإعدادات.\",\n  \"novaSettings.settingsSuccessToast\": \"تم حفظ الإعدادات\",\n  \"novaSettings.general\": \"الإعدادات العامة\"\n}\n"
  },
  {
    "path": "lang/de.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Einstellungen\",\n  \"novaSettings.saveButtonText\": \"Einstellungen speichern\",\n  \"novaSettings.noSettingsFieldsText\": \"Es wurden keine Einstellungs-Felder definiert.\",\n  \"novaSettings.settingsSuccessToast\": \"Einstellungen erfolgreich geändert.\"\n}\n"
  },
  {
    "path": "lang/en.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Settings\",\n  \"novaSettings.saveButtonText\": \"Save settings\",\n  \"novaSettings.noSettingsFieldsText\": \"No settings fields have been defined.\",\n  \"novaSettings.settingsSuccessToast\": \"Settings successfully updated!\",\n  \"novaSettings.general\": \"General\"\n}\n"
  },
  {
    "path": "lang/es.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Configuración\",\n  \"novaSettings.saveButtonText\": \"Guardar configuración\",\n  \"novaSettings.noSettingsFieldsText\": \"No se ha definido ningún campo de configuración.\",\n  \"novaSettings.settingsSuccessToast\": \"¡La configuración se ha actualizado correctamente!\",\n  \"novaSettings.general\": \"General\"\n}\n"
  },
  {
    "path": "lang/et.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Seaded\",\n  \"novaSettings.saveButtonText\": \"Salvesta seaded\",\n  \"novaSettings.noSettingsFieldsText\": \"Ühtegi seadevälja ei ole defineeritud.\",\n  \"novaSettings.settingsSuccessToast\": \"Seaded uuendatud!\",\n  \"novaSettings.general\": \"Üldine\"\n}\n"
  },
  {
    "path": "lang/fa.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"تنظیمات\",\n  \"novaSettings.saveButtonText\": \"ذخیره تنظیمات\",\n  \"novaSettings.noSettingsFieldsText\": \"هیچ تنظیمی تعریف نشده است.\",\n  \"novaSettings.settingsSuccessToast\": \"تنظیمات ذخیره شد.\",\n  \"novaSettings.general\": \"عمومی\"\n}\n"
  },
  {
    "path": "lang/fr.json",
    "content": "{\r\n    \"novaSettings.navigationItemTitle\": \"Paramètres\",\r\n    \"novaSettings.saveButtonText\": \"Enregistrer paramètres\",\r\n    \"novaSettings.noSettingsFieldsText\": \"Aucun champ paramètres n'a été défini.\",\r\n    \"novaSettings.settingsSuccessToast\": \"Paramètres mis à jour !\",\r\n    \"novaSettings.general\": \"Paramètres généraux\"\r\n  }\r\n  "
  },
  {
    "path": "lang/it.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Impostazioni\",\n  \"novaSettings.saveButtonText\": \"Salva impostazioni\",\n  \"novaSettings.noSettingsFieldsText\": \"Nessun campo Impostazioni è stato definito.\",\n  \"novaSettings.settingsSuccessToast\": \"Impostazioni aggiornate con successo\",\n  \"novaSettings.general\": \"Generale\"\n}\n"
  },
  {
    "path": "lang/nl.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Instellingen\",\n  \"novaSettings.saveButtonText\": \"Instellingen opslaan\",\n  \"novaSettings.noSettingsFieldsText\": \"Geen veld gedefineerd.\",\n  \"novaSettings.settingsSuccessToast\": \"Instellingen succesvol geupdatet\",\n  \"novaSettings.general\": \"Algemeen\"\n}\n"
  },
  {
    "path": "lang/pt-BR.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Configurações\",\n  \"novaSettings.saveButtonText\": \"Salvar configurações\",\n  \"novaSettings.noSettingsFieldsText\": \"Nenhum campo de configuração foi definido.\",\n  \"novaSettings.settingsSuccessToast\": \"Configurações atualizadas com sucesso\",\n  \"novaSettings.general\": \"Geral\"\n}\n"
  },
  {
    "path": "lang/ru.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Настройки\",\n  \"novaSettings.saveButtonText\": \"Сохранить\",\n  \"novaSettings.noSettingsFieldsText\": \"Поля настроек не определены.\",\n  \"novaSettings.settingsSuccessToast\": \"Настройки успешно обновлены\",\n  \"novaSettings.general\": \"Основные\"\n}\n"
  },
  {
    "path": "lang/sk.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Nastavenia\",\n  \"novaSettings.saveButtonText\": \"Uložiť nastavenia\",\n  \"novaSettings.noSettingsFieldsText\": \"Neboli definované žiadne polia nastavení.\",\n  \"novaSettings.settingsSuccessToast\": \"Nastavenia boli úspešne aktualizované!\",\n  \"novaSettings.general\": \"Všeobecné\"\n}\n"
  },
  {
    "path": "lang/tr.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Ayarlar\",\n  \"novaSettings.saveButtonText\": \"Ayarları Kaydet\",\n  \"novaSettings.noSettingsFieldsText\": \"Henüz bir ayar alanı tanımlamadınız.\",\n  \"novaSettings.settingsSuccessToast\": \"Ayarlar başarıyla güncellendi!\",\n  \"novaSettings.general\": \"Genel\"\n}\n"
  },
  {
    "path": "lang/uz.json",
    "content": "{\n  \"novaSettings.navigationItemTitle\": \"Sozlamalar\",\n  \"novaSettings.saveButtonText\": \"Saqlash\",\n  \"novaSettings.noSettingsFieldsText\": \"Sozlamalar maydonlari belgilanmagan.\",\n  \"novaSettings.settingsSuccessToast\": \"Sozlamalar muvaffaqiyatli saqlandi\",\n  \"novaSettings.general\": \"Umumiy\"\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"mix\",\n    \"watch\": \"mix watch\",\n    \"hot\": \"mix watch --hot\",\n    \"prod\": \"mix --production\",\n    \"format\": \"prettier --write 'resources/**/*.{css,js,vue}'\"\n  },\n  \"devDependencies\": {\n    \"laravel-mix\": \"^6.0.49\",\n    \"@vue/babel-plugin-jsx\": \"^1.2.5\",\n    \"cross-env\": \"^7.0.3\",\n    \"laravel-nova-devtool\": \"file:vendor/laravel/nova-devtool\",\n    \"prettier\": \"^3.4.2\",\n    \"resolve-url-loader\": \"^5.0.0\",\n    \"sass\": \"^1.83.0\",\n    \"sass-loader\": \"^16.0.4\",\n    \"terser-webpack-plugin\": \"^5.3.11\",\n    \"vue-loader\": \"^16.8.3\",\n    \"vue-template-compiler\": \"^2.7.16\",\n    \"vuex\": \"^4.1.0\"\n  },\n  \"dependencies\": {\n    \"vue\": \"^3.5.13\"\n  }\n}\n"
  },
  {
    "path": "phpunit.dusk.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\" bootstrap=\"tests/bootstrap.php\" colors=\"true\">\n  <coverage processUncoveredFiles=\"true\">\n    <include>\n      <directory suffix=\".php\">./src</directory>\n    </include>\n  </coverage>\n  <testsuites>\n    <testsuite name=\"Browser Test Suite\">\n      <directory suffix=\"Test.php\">./tests/Browser</directory>\n    </testsuite>\n  </testsuites>\n  <php>\n    <env name=\"APP_URL\" value=\"http://127.0.0.1:8085\"/>\n    <env name=\"APP_ENV\" value=\"testing\"/>\n    <env name=\"APP_KEY\" value=\"base64:+Yk3U4u5A5sSnN6kUr5b38I2LE/SgNxwov5XX5FvSW0=\"/>\n    <env name=\"APP_DEBUG\" value=\"true\"/>\n    <env name=\"DUSK_TIMEZONE\" value=\"Europe/Tallinn\"/>\n    <env name=\"DUSK_WIDTH\" value=\"1920\"/>\n    <env name=\"DUSK_HEIGHT\" value=\"1080\"/>\n  </php>\n</phpunit>\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" bootstrap=\"vendor/autoload.php\" backupGlobals=\"false\" backupStaticAttributes=\"false\" colors=\"true\" verbose=\"true\" convertErrorsToExceptions=\"true\" convertNoticesToExceptions=\"true\" convertWarningsToExceptions=\"true\" processIsolation=\"false\" stopOnFailure=\"false\" xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\">\n  <coverage>\n    <include>\n      <directory suffix=\".php\">src/</directory>\n    </include>\n  </coverage>\n  <testsuites>\n    <testsuite name=\"Test Suite\">\n      <directory>./tests/Feature</directory>\n    </testsuite>\n  </testsuites>\n</phpunit>\n"
  },
  {
    "path": "resources/js/entry.js",
    "content": "Nova.booting((Vue, router, store) => {\n  Nova.inertia('NovaSettings', require('./views/Settings').default);\n});\n"
  },
  {
    "path": "resources/js/views/Settings.vue",
    "content": "<template>\n  <LoadingView :loading=\"loading\" :key=\"pageId\">\n    <Head :title=\"__('novaSettings.navigationItemTitle') + (pageId !== 'general' ? ` (${pageId})` : '')\" />\n\n    <form v-if=\"fields && fields.length\" @submit.prevent=\"update\" autocomplete=\"off\" dusk=\"nova-settings-form\">\n      <template v-for=\"panel in panelsWithFields\" :key=\"panel.name\">\n        <component\n          :is=\"`form-` + panel.component\"\n          :panel=\"panel\"\n          :name=\"panel.name\"\n          :fields=\"panel.fields\"\n          :resource-name=\"'nova-settings'\"\n          :resource-id=\"pageId\"\n          mode=\"form\"\n          class=\"mb-6\"\n          :validation-errors=\"validationErrors\"\n          :show-help-text=\"true\"\n        />\n      </template>\n      <!-- Update Button -->\n      <div class=\"flex items-center\" v-if=\"authorizations.authorizedToUpdate\">\n        <Button dusk=\"update-button\" type=\"submit\" class=\"ml-auto\" :disabled=\"isUpdating\" :loading=\"isUpdating\">\n          {{ __('novaSettings.saveButtonText') }}\n        </Button>\n      </div>\n    </form>\n\n    <div class=\"bg-white dark:bg-gray-800 rounded-lg shadow p-3\" v-else>\n      <div class=\"flex flex-col justify-center align-center\">\n        <div class=\"w-3/4 py-4 text-center\">\n          <p class=\"text-90\">{{ __('novaSettings.noSettingsFieldsText') }}</p>\n        </div>\n      </div>\n    </div>\n  </LoadingView>\n</template>\n\n<script>\nimport { Errors } from 'laravel-nova';\nimport { Button } from 'laravel-nova-ui';\n\nexport default {\n  components: { Button },\n\n  data() {\n    return {\n      pageId: false,\n      loading: false,\n      isUpdating: false,\n      fields: [],\n      panels: [],\n      authorizations: [],\n      validationErrors: new Errors(),\n    };\n  },\n  async created() {\n    this.pageId = this.$page.props.pageId || 'general';\n\n    this.getFields();\n  },\n  methods: {\n    async getFields() {\n      this.loading = true;\n      this.fields = [];\n\n      const params = { editing: true, editMode: 'update' };\n      if (this.pageId) params.path = this.pageId;\n\n      const {\n        data: { fields, panels, authorizations },\n      } = await Nova.request()\n        .get('/nova-vendor/nova-settings/settings', { params })\n        .catch(error => {\n          if (error.response.status == 404) {\n            // this.$router.push({ name: '404' });\n            return;\n          }\n        });\n      this.fields = fields;\n      this.panels = panels;\n      this.authorizations = authorizations;\n      this.loading = false;\n\n      // Dispatch event\n      const eventName = this.isUpdating ? 'resource-updated' : 'resource-loaded';\n      Nova.$emit(eventName, {\n        resourceName: 'nova-settings',\n      });\n    },\n    async update() {\n      try {\n        this.isUpdating = true;\n        const response = await this.updateRequest();\n        if (response && response.data) {\n          if (response.data.reload === true) {\n            location.reload();\n            return;\n          } else if (response.data.redirect && response.data.redirect.length > 0) {\n            location.replace(response.data.redirect);\n            return;\n          }\n        }\n        Nova.success(this.__('novaSettings.settingsSuccessToast'));\n\n        // Reset the form by refetching the fields\n        await this.getFields();\n        this.isUpdating = false;\n        this.validationErrors = new Errors();\n      } catch (error) {\n        console.error(error);\n        this.isUpdating = false;\n        if (error && error.response && error.response.status == 422) {\n          this.validationErrors = new Errors(error.response.data.errors);\n          Nova.error(this.__('There was a problem submitting the form.'));\n        }\n      }\n    },\n    updateRequest() {\n      return Nova.request().post('/nova-vendor/nova-settings/settings', this.formData);\n    },\n  },\n  computed: {\n    formData() {\n      const formData = new FormData();\n      this.fields.forEach(field => field.fill(formData));\n      formData.append('_method', 'POST');\n      if (this.pageId) formData.append('path', this.pageId);\n      return formData;\n    },\n    panelsWithFields() {\n      return this.panels.map(panel => {\n        return {\n          name: panel.name,\n          component: panel.component,\n          helpText: panel.helpText,\n          fields: this.fields.filter(field => field.panel === panel.name),\n          showTitle: panel.showTitle,\n        };\n      });\n    },\n  },\n};\n</script>\n"
  },
  {
    "path": "routes/api.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Facades\\Route;\n\n/*\n|--------------------------------------------------------------------------\n| Tool API Routes\n|--------------------------------------------------------------------------\n|\n| Here is where you may register API routes for your tool. These routes\n| are loaded by the ServiceProvider of your tool. They are protected\n| by your tool's \"Authorize\" middleware by default. Now, go build!\n|\n*/\n\nRoute::namespace('\\Outl1ne\\NovaSettings\\Http\\Controllers')->group(function () {\n    Route::prefix('nova-vendor/nova-settings')->group(function () {\n        Route::get('/settings', 'SettingsController@get')->name('nova-settings.get');\n        Route::post('/settings', 'SettingsController@save')->name('nova-settings.save');\n    });\n\n    Route::delete('/nova-api/nova-settings/{path}/field/{fieldName}', 'SettingsController@deleteImage');\n    Route::post('/nova-api/nova-settings/{path}/field/{attribute}/preview', 'SettingsController@fieldPreview')->name('nova-settings.field-preview');\n});\n"
  },
  {
    "path": "src/Http/Controllers/SettingsController.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Http\\Controllers;\n\nuse Laravel\\Nova\\Panel;\nuse Illuminate\\Http\\Request;\nuse Laravel\\Nova\\ResolvesFields;\nuse Illuminate\\Routing\\Controller;\nuse Laravel\\Nova\\Contracts\\Resolvable;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Laravel\\Nova\\Fields\\FieldCollection;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Laravel\\Nova\\Http\\Requests\\NovaRequest;\nuse Illuminate\\Http\\Resources\\ConditionallyLoadsAttributes;\n\nclass SettingsController extends Controller\n{\n    use ResolvesFields, ConditionallyLoadsAttributes;\n\n    public function get(Request $request)\n    {\n        if (!NovaSettings::canSeeSettings()) return $this->unauthorized();\n\n        $path = $request->get('path', 'general');\n        $label = NovaSettings::getPageName($path);\n        $fields = $this->assignToPanels($label, $this->availableFields($path));\n        $panels = $this->panelsWithDefaultLabel($label, app(NovaRequest::class));\n\n        $addResolveCallback = function (&$field) {\n            if (!empty($field->attribute)) {\n                $setting = NovaSettings::getSettingsModel()::firstOrNew(['key' => $field->attribute]);\n                $fakeResource = $this->makeFakeResource($field->attribute, isset($setting) ? $setting->value : '');\n                $field->resolve($fakeResource);\n            }\n\n            if (!empty($field->meta['fields'])) {\n                foreach ($field->meta['fields'] as $_field) {\n                    $setting = NovaSettings::getSettingsModel()::where('key', $_field->attribute)->first();\n                    $fakeResource = $this->makeFakeResource($_field->attribute, isset($setting) ? $setting->value : null);\n                    $_field->resolve($fakeResource);\n                }\n            }\n        };\n\n        $fields->each(function (&$field) use ($addResolveCallback) {\n            $addResolveCallback($field);\n        });\n\n        return response()->json([\n            'panels' => $panels,\n            'fields' => $fields,\n            'authorizations' => NovaSettings::getAuthorizations(),\n        ], 200);\n    }\n\n    public function save(NovaRequest $request)\n    {\n        if (!NovaSettings::getAuthorizations('authorizedToUpdate')) return $this->unauthorized();\n\n        $fields = $this->availableFields($request->get('path', 'general'));\n\n        // NovaDependencyContainer support\n        $fields = $fields->map(function ($field) {\n            if (!empty($field->attribute)) return $field;\n            if (!empty($field->meta['fields'])) return $field->meta['fields'];\n            return null;\n        })->filter()->flatten();\n\n        $rules = [];\n        foreach ($fields as $field) {\n            $fakeResource = $this->makeFakeResource($field->attribute, nova_get_setting($field->attribute));\n            $field->resolve($fakeResource, $field->attribute); // For nova-translatable support\n            $rules = array_merge($rules, $field->getUpdateRules($request));\n        }\n\n        Validator::make($request->all(), $rules)->validate();\n\n        $fields->whereInstanceOf(Resolvable::class)->each(function ($field) use ($request) {\n            if (empty($field->attribute)) return;\n            if ($field->isReadonly(app(NovaRequest::class))) return;\n            $settingsClass = NovaSettings::getSettingsModel();\n\n            // For nova-translatable support\n            if (!empty($field->meta['translatable']['original_attribute'])) $field->attribute = $field->meta['translatable']['original_attribute'];\n\n            $existingRow = $settingsClass::where('key', $field->attribute)->first();\n\n            $tempResource = new \\Laravel\\Nova\\Support\\Fluent;\n            $field->fill($request, $tempResource);\n\n            if (!array_key_exists($field->attribute, $tempResource->getAttributes())) return;\n\n            if (isset($existingRow)) {\n                $existingRow->value = $tempResource->{$field->attribute};\n                $existingRow->save();\n            } else {\n                $newRow = new $settingsClass;\n                $newRow->key = $field->attribute;\n                $newRow->value = $tempResource->{$field->attribute};\n                $newRow->save();\n            }\n        });\n\n        if (config('nova-settings.reload_page_on_save', false) === true) {\n            return response()->json(['reload' => true]);\n        }\n\n        return response('', 204);\n    }\n\n    public function deleteImage(Request $request, $pathName, $fieldName)\n    {\n        if (!NovaSettings::getAuthorizations('authorizedToUpdate')) return $this->unauthorized();\n\n        $existingRow = NovaSettings::getSettingsModel()::where('key', $fieldName)->first();\n        if (isset($existingRow)) {\n            $field = $this->findField(collect(NovaSettings::getFields($pathName)), $fieldName);\n\n            // Delete file if exists\n            if (isset($field) && $field instanceof \\Laravel\\Nova\\Fields\\File) {\n                $disk = $field->getStorageDisk();\n                Storage::disk($disk)->delete($existingRow->value);\n            }\n\n            $existingRow->value = null;\n            $existingRow->save();\n        }\n\n        return response('', 204);\n    }\n\n    /**\n     * Handle field preview requests for markdown and other previewable fields.\n     *\n     * @param  \\Illuminate\\Http\\Request  $request\n     * @param  string  $path\n     * @param  string  $attribute\n     * @return \\Illuminate\\Http\\JsonResponse\n     */\n    public function fieldPreview(Request $request, $path, $attribute)\n    {\n        if (!NovaSettings::canSeeSettings()) return $this->unauthorized();\n\n        // Find the field in the settings\n        $field = $this->findField(collect(NovaSettings::getFields($path)), $attribute);\n\n        if (!$field) {\n            return response()->json(['error' => 'Field not found'], 404);\n        }\n\n        // Get the content to preview from the request\n        $content = $request->input('value', '');\n\n        return response()->json([\n            'preview' => $field->previewFor($content)\n        ]);\n    }\n\n    protected function findField($fields, $fieldName)\n    {\n        if (empty($fields)) return null;\n\n        $field = $fields->firstWhere('attribute', $fieldName);\n\n        // Target field might be inside container field\n        if (empty($field)) {\n            foreach ($fields as $value) {\n                if ($value instanceof \\Laravel\\Nova\\Panel) {\n                    $field = $this->findField(collect($value->data), $fieldName);\n                    if (!empty($field)) return $field;\n                }\n\n                if (class_exists('\\Eminiarts\\Tabs\\Tabs') && $value instanceof \\Eminiarts\\Tabs\\Tabs) {\n                    $field = $this->findField(collect($value->data), $fieldName);\n                    if (!empty($field)) return $field;\n                }\n            }\n        }\n\n        return $field;\n    }\n\n    protected function availableFields($path = 'general')\n    {\n        return (new FieldCollection($this->filter(NovaSettings::getFields($path))))->authorized(request());\n    }\n\n    protected function fields(Request $request, $path = 'general')\n    {\n        return NovaSettings::getFields($path);\n    }\n\n    protected function makeFakeResource(string $fieldName, $fieldValue)\n    {\n        $fakeResource = new \\Laravel\\Nova\\Support\\Fluent;\n        $fakeResource->{$fieldName} = $fieldValue;\n        return $fakeResource;\n    }\n\n    /**\n     * Return the panels for this request with the default label.\n     *\n     * @param  string  $label\n     * @param  \\Laravel\\Nova\\Http\\Requests\\NovaRequest  $request\n     * @return array\n     */\n    protected function panelsWithDefaultLabel($label, NovaRequest $request)\n    {\n        $method = $this->fieldsMethod($request);\n\n        return with(\n            collect(array_values($this->{$method}($request, $request->get('path', 'general'))))->whereInstanceOf(Panel::class)->unique('name')->values(),\n            function ($panels) use ($label) {\n                return $panels->when($panels->where('name', $label)->isEmpty(), function ($panels) use ($label) {\n                    return $panels->prepend((new Panel($label))->withToolbar());\n                })->all();\n            }\n        );\n    }\n\n    protected function unauthorized()\n    {\n        return response()->json(['error' => 'Unauthorized'], 403);\n    }\n\n    protected function assignToPanels($label, FieldCollection $fields)\n    {\n        return $fields->map(function ($field) use ($label) {\n            if (!$field->panel) $field->panel = Panel::make($label);\n            return $field;\n        });\n    }\n}\n"
  },
  {
    "path": "src/Http/Middleware/Authorize.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Http\\Middleware;\n\nuse Laravel\\Nova\\Nova;\nuse Outl1ne\\NovaSettings\\NovaSettings;\n\nclass Authorize\n{\n    /**\n     * Handle the incoming request.\n     *\n     * @param  \\Illuminate\\Http\\Request  $request\n     * @param  \\Closure  $next\n     * @return \\Illuminate\\Http\\Response\n     */\n    public function handle($request, $next)\n    {\n        $tool = collect(Nova::registeredTools())->first([$this, 'matchesTool']);\n\n        return optional($tool)->authorize($request) ? $next($request) : abort(403);\n    }\n\n    /**\n     * Determine whether this tool belongs to the package.\n     *\n     * @param \\Laravel\\Nova\\Tool $tool\n     * @return bool\n     */\n    public function matchesTool($tool)\n    {\n        return $tool instanceof NovaSettings;\n    }\n}\n"
  },
  {
    "path": "src/Http/Middleware/SettingsPathExists.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Http\\Middleware;\n\n\nuse Outl1ne\\NovaSettings\\NovaSettings;\n\nclass SettingsPathExists\n{\n    /**\n     * Handle the incoming request.\n     *\n     * @param  \\Illuminate\\Http\\Request  $request\n     * @param  \\Closure  $next\n     * @return \\Illuminate\\Http\\Response\n     */\n    public function handle($request, $next)\n    {\n        $path = $request->get('path') ?: $request->route('path');\n        $path = !empty($path) ? trim($path) : 'general';\n        return NovaSettings::doesPathExist($path) ? $next($request) : abort(404);\n    }\n}\n"
  },
  {
    "path": "src/Models/Settings.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Models;\n\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Settings extends Model\n{\n    protected $primaryKey = 'key';\n    public $incrementing = false;\n    public $timestamps = false;\n    public $fillable = ['key', 'value'];\n\n    public function __construct(array $attributes = [])\n    {\n        parent::__construct($attributes);\n        $this->setTable(NovaSettings::getSettingsTableName());\n    }\n\n    protected static function booted()\n    {\n        static::updated(function ($setting) {\n            NovaSettings::getStore()->clearCache($setting->key);\n        });\n    }\n\n    public function setValueAttribute($value)\n    {\n        $this->casts = NovaSettings::getCasts();\n\n        $castType = null;\n        if ($this->hasCast($this->key)) $castType = $this->getCastType($this->key);\n\n        switch ($castType) {\n            case 'datetime':\n            case 'date':\n                $this->attributes['value'] = $value;\n                return;\n\n            default:\n                $this->attributes['value'] = is_array($value) || $value instanceof \\JsonSerializable\n                    ? json_encode($value)\n                    : $value;\n        }\n    }\n\n    public function getValueAttribute($value)\n    {\n        $originalCasts = $this->casts;\n        $this->casts = NovaSettings::getCasts();\n\n        if ($this->hasCast($this->key)) {\n            $value = $this->castAttribute($this->key, $value);\n        }\n\n        $this->casts = $originalCasts;\n\n        return $value;\n    }\n\n    public static function getValueForKey($key)\n    {\n        $setting = static::where('key', $key)->get()->first();\n        return isset($setting) ? $setting->value : null;\n    }\n}\n"
  },
  {
    "path": "src/Nova/Resources/Settings.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Nova\\Resources;\n\nuse Laravel\\Nova\\Resource;\nuse Illuminate\\Http\\Request;\nuse Outl1ne\\NovaSettings\\NovaSettings;\n\nclass Settings extends Resource\n{\n    public static $title = 'key';\n    public static $model = null;\n    public static $displayInNavigation = false;\n\n    public function __construct($resource)\n    {\n        self::$model = NovaSettings::getSettingsModel();\n        parent::__construct($resource);\n    }\n\n    public function fields(Request $request)\n    {\n        return [];\n    }\n}\n"
  },
  {
    "path": "src/NovaSettings.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings;\n\nuse Laravel\\Nova\\Nova;\nuse Laravel\\Nova\\Tool;\nuse Illuminate\\Support\\Str;\nuse Illuminate\\Http\\Request;\nuse Laravel\\Nova\\Menu\\MenuItem;\nuse Laravel\\Nova\\Menu\\MenuSection;\nuse Outl1ne\\NovaSettings\\Models\\Settings;\n\nclass NovaSettings extends Tool\n{\n    public function boot()\n    {\n        Nova::script('nova-settings', __DIR__ . '/../dist/js/entry.js');\n    }\n\n    public function menu(Request $request)\n    {\n        $fields = static::getFields();\n        $basePath = config('nova-settings.base_path', 'nova-settings');\n        $isAuthorized = static::canSeeSettings();\n        $showInSidebar = config('nova-settings.show_in_sidebar', true);\n\n        if (!$isAuthorized || !$showInSidebar || empty($fields)) return null;\n\n        if (count($fields) == 1) {\n            return MenuSection::make(__('novaSettings.navigationItemTitle'))\n                ->path($basePath . '/' . array_key_first($fields))\n                ->icon('adjustments-vertical');\n        } else {\n            $menuItems = [];\n            foreach ($fields as $key => $fields) {\n                $menuItems[] = MenuItem::link(self::getPageName($key), \"{$basePath}/{$key}\");\n            }\n\n            return MenuSection::make(__('novaSettings.navigationItemTitle'), $menuItems)\n                ->icon('adjustments-vertical')\n                ->collapsable();\n        }\n    }\n\n    public static function getSettingsTableName(): string\n    {\n        return config('nova-settings.table', 'nova_settings');\n    }\n\n    public static function getPageName($key): string\n    {\n        if (__(\"novaSettings.$key\") === \"novaSettings.$key\") {\n            return Str::title(str_replace('-', ' ', $key));\n        } else {\n            return __(\"novaSettings.$key\");\n        }\n    }\n\n    public static function getAuthorizations($key = null)\n    {\n        $request = request();\n        $fakeResource = new \\Outl1ne\\NovaSettings\\Nova\\Resources\\Settings(NovaSettings::getSettingsModel()::make());\n\n        $authorizations = [\n            'authorizedToView' => $fakeResource->authorizedToView($request),\n            'authorizedToCreate' => $fakeResource->authorizedToCreate($request),\n            'authorizedToUpdate' => $fakeResource->authorizedToUpdate($request),\n            'authorizedToDelete' => $fakeResource->authorizedToDelete($request),\n        ];\n\n        return $key ? $authorizations[$key] : $authorizations;\n    }\n\n    public static function canSeeSettings()\n    {\n        $auths = static::getAuthorizations();\n        return $auths['authorizedToView'] || $auths['authorizedToUpdate'];\n    }\n\n    /**\n     * Define settings fields and an optional casts.\n     *\n     * @param array|callable $fields Array of fields/panels to be displayed or callable that returns an array.\n     * @param array $casts Associative array same as Laravel's $casts on models.\n     **/\n    public static function addSettingsFields($fields = [], $casts = [], $path = 'general')\n    {\n        return static::getStore()->addSettingsFields($fields, $casts, $path);\n    }\n\n    /**\n     * Define casts.\n     *\n     * @param array $casts Casts same as Laravel's casts on a model.\n     **/\n    public static function addCasts($casts = [])\n    {\n        return static::getStore()->addCasts($casts);\n    }\n\n    public static function getFields($path = null)\n    {\n        if (!$path) return static::getStore()->getRawFields();\n        return static::getStore()->getFields($path);\n    }\n\n    public static function clearFields()\n    {\n        return static::getStore()->clearFields();\n    }\n\n    public static function getCasts()\n    {\n        return static::getStore()->getCasts();\n    }\n\n    public static function getSetting($settingKey, $default = null)\n    {\n        return static::getStore()->getSetting($settingKey, $default);\n    }\n\n    public static function getSettings(?array $settingKeys = null, array $defaults = [])\n    {\n        return static::getStore()->getSettings($settingKeys, $defaults);\n    }\n\n    public static function setSettingValue($settingKey, $value = null)\n    {\n        return static::getStore()->setSettingValue($settingKey, $value);\n    }\n\n    public static function getSettingsModel(): string\n    {\n        return config('nova-settings.models.settings', Settings::class);\n    }\n\n    public static function doesPathExist($path)\n    {\n        return array_key_exists($path, static::getFields());\n    }\n\n    public static function getStore(): NovaSettingsStore\n    {\n        return app()->make(NovaSettingsStore::class);\n    }\n}\n"
  },
  {
    "path": "src/NovaSettingsCacheStore.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings;\n\nuse Illuminate\\Support\\Facades\\Cache;\n\nuse function collect;\nuse function is_array;\nuse function is_string;\n\nclass NovaSettingsCacheStore extends NovaSettingsStore\n{\n    /** @var \\Illuminate\\Contracts\\Cache\\Repository */\n    private $cache;\n\n    public function __construct()\n    {\n        $this->cache = Cache::store(config('nova-settings.cache.store'));\n    }\n\n    public function clearCache($keyNames = null)\n    {\n        // Clear whole cache\n        if (empty($keyNames)) {\n            $this->getSettingsModelClass()::all(['key'])->each(function ($setting) {\n                $this->cache->forget($this->getCacheKey($setting->key));\n            });\n\n            return;\n        }\n\n        // Clear specific keys\n        if (is_string($keyNames)) $keyNames = [$keyNames];\n        foreach ($keyNames as $key) {\n            $this->cache->forget($this->getCacheKey($key));\n        }\n    }\n\n    protected function getCached($keyNames = null)\n    {\n        if (is_string($keyNames)) {\n            return $this->cache->get($this->getCacheKey($keyNames));\n        }\n\n        if (is_array($keyNames)) {\n            return collect($keyNames)\n                ->mapWithKeys(function ($key) {\n                    if (!$this->cache->has($this->getCacheKey($key))) return [];\n\n                    return [$key => $this->getCached($key)];\n                })\n                ->toArray();\n        }\n\n        return [];\n    }\n\n    protected function setCached($keyName, $value)\n    {\n        $this->cache->forever($this->getCacheKey($keyName), $value);\n    }\n\n    private function getCacheKey($key)\n    {\n        return config('nova-settings.cache.prefix', 'nova-settings:') . $key;\n    }\n}\n"
  },
  {
    "path": "src/NovaSettingsInMemoryStore.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings;\n\nuse function collect;\nuse function is_array;\nuse function is_string;\n\nclass NovaSettingsInMemoryStore extends NovaSettingsStore\n{\n    protected $cache = [];\n\n    public function clearCache($keyNames = null)\n    {\n        // Clear whole cache\n        if (empty($keyNames)) {\n            $this->cache = [];\n            return;\n        }\n\n        // Clear specific keys\n        if (is_string($keyNames)) $keyNames = [$keyNames];\n        foreach ($keyNames as $key) {\n            unset($this->cache[$key]);\n        }\n    }\n\n    protected function getCached($keyNames = null)\n    {\n        if (is_string($keyNames)) return $this->cache[$keyNames] ?? null;\n\n        return is_array($keyNames) && !empty($keyNames)\n            ? collect($this->cache)->only($keyNames)->toArray()\n            : $this->cache;\n    }\n\n    protected function setCached($keyName, $value)\n    {\n        $this->cache[$keyName] = $value;\n    }\n}\n"
  },
  {
    "path": "src/NovaSettingsNoCacheStore.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings;\n\nuse function is_string;\n\nclass NovaSettingsNoCacheStore extends NovaSettingsStore\n{\n    public function clearCache($keyNames = null)\n    {\n    }\n\n    protected function getCached($keyNames = null)\n    {\n        if (is_string($keyNames)) return null;\n\n        return [];\n    }\n\n    protected function setCached($keyName, $value)\n    {\n    }\n}\n"
  },
  {
    "path": "src/NovaSettingsServiceProvider.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings;\n\nuse Laravel\\Nova\\Nova;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\ServiceProvider;\nuse Laravel\\Nova\\Http\\Middleware\\Authenticate;\nuse Outl1ne\\NovaSettings\\Http\\Middleware\\Authorize;\nuse Outl1ne\\NovaTranslationsLoader\\LoadsNovaTranslations;\nuse Outl1ne\\NovaSettings\\Http\\Middleware\\SettingsPathExists;\n\nuse function array_keys;\nuse function config;\nuse function config_path;\nuse function database_path;\nuse function in_array;\nuse function inertia;\nuse function is_array;\n\nclass NovaSettingsServiceProvider extends ServiceProvider\n{\n    use LoadsNovaTranslations;\n\n    /**\n     * Bootstrap any application services.\n     *\n     * @return void\n     */\n    public function boot()\n    {\n        $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');\n        $this->loadTranslations(__DIR__ . '/../lang', 'nova-settings', true);\n\n        if ($this->app->runningInConsole()) {\n            // Publish migrations\n            $this->publishes([\n                __DIR__ . '/../database/migrations' => database_path('migrations'),\n            ], 'migrations');\n\n            // Publish config\n            $this->publishes([\n                __DIR__ . '/../config/' => config_path(),\n            ], 'config');\n        }\n    }\n\n    public function register()\n    {\n        $this->registerRoutes();\n\n        $this->mergeConfigFrom(\n            __DIR__ . '/../config/nova-settings.php',\n            'nova-settings'\n        );\n\n        $this->registerSettingsStore();\n    }\n\n    protected function registerSettingsStore()\n    {\n        $caching = config('nova-settings.cache.store');\n\n        if (is_array(config('cache.stores')) && in_array($caching, array_keys(config('cache.stores')))) {\n            $this->app->singleton(NovaSettingsStore::class, function () {\n                return new NovaSettingsCacheStore();\n            });\n        } else if ($caching === ':memory:') {\n            $this->app->singleton(NovaSettingsStore::class, function () {\n                return new NovaSettingsInMemoryStore();\n            });\n        } else {\n            $this->app->singleton(NovaSettingsStore::class, function () {\n                return new NovaSettingsNoCacheStore();\n            });\n        }\n    }\n\n    protected function registerRoutes()\n    {\n        // Register nova routes\n        Nova::router()->group(function ($router) {\n            $path = config('nova-settings.base_path', 'nova-settings');\n\n            $router\n                ->get(\"{$path}/{pageId?}\", fn ($pageId = 'general') => inertia('NovaSettings', ['basePath' => $path, 'pageId' => $pageId]))\n                ->middleware(['nova', Authenticate::class])\n                ->domain(config('nova.domain', null));\n        });\n\n        if ($this->app->routesAreCached()) return;\n\n        Route::middleware(['nova', Authorize::class, SettingsPathExists::class])\n            ->domain(config('nova.domain', null))\n            ->group(__DIR__ . '/../routes/api.php');\n    }\n}\n"
  },
  {
    "path": "src/NovaSettingsStore.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings;\n\nuse Illuminate\\Support\\Str;\n\nuse function array_diff;\nuse function array_keys;\nuse function array_map;\nuse function array_merge;\nuse function call_user_func;\nuse function collect;\nuse function is_array;\nuse function is_callable;\n\nabstract class NovaSettingsStore\n{\n    protected $fields = [];\n    protected $casts = [];\n\n    public function addSettingsFields($fields = [], $casts = [], $path = 'general')\n    {\n        $path = Str::lower(Str::slug($path));\n\n        if (is_callable($fields)) $fields = [$fields];\n        $this->fields[$path] = array_merge($this->fields[$path] ?? [], $fields ?? []);\n\n        $this->casts = array_merge($this->casts, $casts ?? []);\n\n        return $this;\n    }\n\n    public function addCasts($casts = [])\n    {\n        $this->casts = array_merge($this->casts, $casts);\n        return $this;\n    }\n\n    public function getRawFields()\n    {\n        return $this->fields;\n    }\n\n    public function getFields($path = 'general')\n    {\n        $rawFields = array_map(function ($fieldItem) {\n            return is_callable($fieldItem) ? call_user_func($fieldItem) : $fieldItem;\n        }, $this->fields[$path] ?? $this->fields);\n\n        $fields = [];\n        foreach ($rawFields as $rawField) {\n            if (is_array($rawField)) $fields = array_merge($fields, $rawField);\n            else $fields[] = $rawField;\n        }\n\n        return $fields;\n    }\n\n    public function getCasts()\n    {\n        return $this->casts;\n    }\n\n    public function getSetting($settingKey, $default = null)\n    {\n        if ($cached = $this->getCached($settingKey)) return $cached;\n\n        $settingValue = $this->getSettingsModelClass()::getValueForKey($settingKey) ?? $default;\n\n        $this->setCached($settingKey, $settingValue);\n\n        return $settingValue;\n    }\n\n    public function getSettings(?array $settingKeys = null, array $defaults = [])\n    {\n        if (!empty($settingKeys)) {\n            $cached = $this->getCached($settingKeys);\n\n            $hasMissingKeys = !empty(array_diff($settingKeys, array_keys($cached)));\n\n            if (!$hasMissingKeys) return $cached;\n\n            $settings = $this->getSettingsModelClass()::whereIn('key', $settingKeys)\n                ->get()\n                ->pluck('value', 'key');\n\n            return collect($settingKeys)->flatMap(function ($settingKey) use ($settings, $defaults) {\n                $settingValue = $settings[$settingKey] ?? null;\n\n                if (isset($settingValue)) {\n                    $this->setCached($settingKey, $settingValue);\n                    return [$settingKey => $settingValue];\n                } else {\n                    $defaultValue = $defaults[$settingKey] ?? null;\n                    return [$settingKey => $defaultValue];\n                }\n            })->toArray();\n        }\n\n        return $this->getSettingsModelClass()::all()\n            ->tap(function ($settings) {\n                $settings->each(function ($setting) {\n                    $this->setCached($setting->key, $setting->value);\n                });\n            })\n            ->pluck('value', 'key')\n            ->toArray();\n    }\n\n    public function setSettingValue($settingKey, $value = null)\n    {\n        $setting = $this->getSettingsModelClass()::firstOrCreate(['key' => $settingKey]);\n        $setting->value = $value;\n        $setting->save();\n\n        $this->setCached($settingKey, $setting->value);\n\n        return $setting;\n    }\n\n    public abstract function clearCache($keyNames = null);\n\n    public function clearFields()\n    {\n        $this->fields = [];\n        $this->casts = [];\n\n        $this->clearCache();\n    }\n\n    protected abstract function getCached($keyNames = null);\n\n    protected abstract function setCached($keyName, $value);\n\n    /**\n     * @return class-string<\\Outl1ne\\NovaSettings\\Models\\Settings>\n     */\n    protected function getSettingsModelClass()\n    {\n        return NovaSettings::getSettingsModel();\n    }\n}\n"
  },
  {
    "path": "src/helpers.php",
    "content": "<?php\n\nuse Outl1ne\\NovaSettings\\NovaSettings;\n\nif (!function_exists('nova_get_settings')) {\n    function nova_get_settings($settingKeys = null, $defaults = [])\n    {\n        return NovaSettings::getSettings($settingKeys, $defaults);\n    }\n}\n\nif (!function_exists('nova_get_setting')) {\n    function nova_get_setting($settingKey, $default = null)\n    {\n        return NovaSettings::getSetting($settingKey, $default);\n    }\n}\n\nif (!function_exists('nova_set_setting_value')) {\n    function nova_set_setting_value($settingKey, $value = null)\n    {\n        return NovaSettings::setSettingValue($settingKey, $value);\n    }\n}\n"
  },
  {
    "path": "testbench.yaml",
    "content": "laravel: ./vendor/laravel/nova-dusk-suite\n\nproviders:\n  - Laravel\\Nova\\NovaServiceProvider\n"
  },
  {
    "path": "tests/Browser/DetailTest.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests\\Browser;\n\nuse App\\Models\\User;\nuse Laravel\\Dusk\\Browser;\nuse Outl1ne\\NovaSettings\\Tests\\DuskTestCase;\n\nclass DetailTest extends DuskTestCase\n{\n    public function test_settings_appears_in_sidebar_with_no_fields()\n    {\n        $this->setupLaravel();\n\n        $this->browse(function (Browser $browser) {\n            $browser->loginAs(User::find(1))\n                ->visit('nova');\n\n            dump($browser->element('*')->getAttribute('innerHTML'));\n\n            $browser\n                ->assertSee('Settings');\n\n            $browser->blank();\n        });\n    }\n\n    public function test_settings_appears_in_sidebar_with_fields()\n    {\n        $this->setupLaravel();\n\n        $this->browse(function (Browser $browser) {\n            $browser->loginAs(User::find(1))\n                ->visit('nova')\n                ->assertSee('Settings');\n\n            $browser->blank();\n        });\n    }\n\n    public function test_can_navigate_into_and_render_settings()\n    {\n        $this->browse(function (Browser $browser) {\n            $browser->loginAs(User::find(1))\n                ->visit('nova')\n                ->assertVisible('@nova-settings')\n                ->pause(1500)\n                ->click('@nova-settings')\n                ->waitFor('@nova-settings-form')\n                ->assertSee('Hello Field');\n\n            $browser->blank();\n        });\n    }\n}\n"
  },
  {
    "path": "tests/DuskTestCase.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests;\n\nuse Laravel\\Nova\\Nova;\nuse Laravel\\Dusk\\Browser;\nuse Laravel\\Nova\\Fields\\Text;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Illuminate\\Foundation\\Application;\n\nabstract class DuskTestCase extends \\Orchestra\\Testbench\\Dusk\\TestCase\n{\n    /**\n     * The base serve host URL to use while testing the application.\n     *\n     * @var string\n     */\n    protected static $baseServeHost = '127.0.0.1';\n\n    /**\n     * The base serve port to use while testing the application.\n     *\n     * @var int\n     */\n    protected static $baseServePort = 8085;\n\n    /**\n     * Server specific setup. It may share alot with the main setUp() method, but\n     * should exclude things like DB migrations so we don't end up wiping the\n     * DB content mid test. Using this method means we can be explicit.\n     *\n     * @return void\n     */\n    protected function setUpDuskServer(): void\n    {\n        parent::setUp();\n\n        Nova::tools([\n            new NovaSettings(),\n        ]);\n\n        NovaSettings::addSettingsFields([\n            Text::make('Hello Field', 'hello_field'),\n        ]);\n\n        tap($this->app->make('config'), function ($config) {\n            $config->set('app.url', static::baseServeUrl());\n            $config->set('filesystems.disks.public.url', static::baseServeUrl() . '/storage');\n        });\n    }\n\n    /**\n     * Get base path.\n     *\n     * @return string\n     */\n    protected function getBasePath()\n    {\n        return realpath(__DIR__ . '/../vendor/laravel/nova-dusk-suite');\n    }\n\n    /**\n     * Get package providers.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     *\n     * @return array\n     */\n    protected function getPackageProviders($app)\n    {\n        return [\n            'Fideloper\\Proxy\\TrustedProxyServiceProvider',\n            'Laravel\\Nova\\NovaCoreServiceProvider',\n            'Carbon\\Laravel\\ServiceProvider',\n            'Outl1ne\\NovaSettings\\NovaSettingsServiceProvider',\n        ];\n    }\n\n    /**\n     * Get application aliases.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     *\n     * @return array\n     */\n    protected function getApplicationAliases($app)\n    {\n        return $app['config']['app.aliases'];\n    }\n\n    /**\n     * Get application providers.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     *\n     * @return array\n     */\n    protected function getApplicationProviders($app)\n    {\n        return $app['config']['app.providers'];\n    }\n\n    /**\n     * Resolve application implementation.\n     *\n     * @return \\Illuminate\\Foundation\\Application\n     */\n    protected function resolveApplication()\n    {\n        return tap(new Application($this->getBasePath()), function ($app) {\n            $app->detectEnvironment(function () {\n                return 'testing';\n            });\n        });\n    }\n\n    /**\n     * Resolve application Console Kernel implementation.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     *\n     * @return void\n     */\n    protected function resolveApplicationConsoleKernel($app)\n    {\n        $app->singleton('Illuminate\\Contracts\\Console\\Kernel', 'App\\Console\\Kernel');\n    }\n\n    /**\n     * Resolve application HTTP Kernel implementation.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     *\n     * @return void\n     */\n    protected function resolveApplicationHttpKernel($app)\n    {\n        $app->singleton('Illuminate\\Contracts\\Http\\Kernel', 'App\\Http\\Kernel');\n    }\n\n    /**\n     * Resolve application HTTP exception handler.\n     *\n     * @param  \\Illuminate\\Foundation\\Application  $app\n     *\n     * @return void\n     */\n    protected function resolveApplicationExceptionHandler($app)\n    {\n        $app->singleton('Illuminate\\Contracts\\Debug\\ExceptionHandler', 'App\\Exceptions\\Handler');\n    }\n\n    /**\n     * Setup Laravel for the test.\n     *\n     * @param  callable|null  $callback\n     * @return void\n     */\n    protected function setupLaravel(callable $callback = null)\n    {\n        $this->artisan('migrate:fresh')->run();\n        $this->artisan('db:seed', ['--class' => \\Database\\Seeders\\DatabaseSeeder::class])->run();\n\n        if (is_callable($callback)) {\n            $callback($this->app);\n        }\n    }\n\n    /**\n     * Run the given callback with searchable functionality enabled.\n     *\n     * @param  callable  $callback\n     * @return void\n     */\n    protected function whileSearchable(callable $callback)\n    {\n        touch(base_path('.searchable'));\n\n        try {\n            $callback();\n        } finally {\n            @unlink(base_path('.searchable'));\n        }\n    }\n\n    /**\n     * Run the given callback with inline-create functionality enabled.\n     *\n     * @param  callable  $callback\n     * @return void\n     */\n    protected function whileInlineCreate(callable $callback)\n    {\n        touch(base_path('.inline-create'));\n\n        try {\n            $callback();\n        } finally {\n            @unlink(base_path('.inline-create'));\n        }\n    }\n\n    /**\n     * Create a new Browser instance.\n     *\n     * @param  \\Facebook\\WebDriver\\Remote\\RemoteWebDriver  $driver\n     * @return \\Laravel\\Dusk\\Browser\n     */\n    protected function newBrowser($driver)\n    {\n        return tap(new Browser($driver), function ($browser) {\n            $browser->resize(env('DUSK_WIDTH'), env('DUSK_HEIGHT'));\n        });\n    }\n\n    protected function captureFailuresFor($browsers)\n    {\n        $browsers->each(function (Browser $browser, $key) {\n            $name = str_replace('\\\\', '_', get_class($this)) . '_' . $this->getName(false);\n            $browser->screenshot('failure-' . $this->getName() . '-' . $key);\n        });\n    }\n}\n"
  },
  {
    "path": "tests/Feature/NavigationTest.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests\\Feature;\n\nuse Laravel\\Nova\\Fields\\Text;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Outl1ne\\NovaSettings\\Tests\\IntegrationTestCase;\n\nclass NavigationTest extends IntegrationTestCase\n{\n    public function test_general_navigation_renders_with_no_fields()\n    {\n        $settingsTool = new NovaSettings;\n        $navigationView = $settingsTool->renderNavigation()->render();\n        $this->assertStringContainsString('dusk=\"nova-settings\"', $navigationView);\n    }\n\n    public function test_general_navigation_renders_with_fields()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('Test'),\n        ]);\n\n        $settingsTool = new NovaSettings;\n        $navigationView = $settingsTool->renderNavigation()->render();\n\n        $this->assertStringContainsString('dusk=\"nova-settings\"', $navigationView);\n    }\n\n    public function test_multiple_navigation_renders()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('Test'),\n        ]);\n\n        NovaSettings::addSettingsFields([\n            Text::make('TestTwo'),\n        ], [], 'Other');\n\n        $settingsTool = new NovaSettings;\n        $navigationView = $settingsTool->renderNavigation()->render();\n\n        $this->assertStringContainsString('dusk=\"nova-settings-general\"', $navigationView);\n        $this->assertStringContainsString('dusk=\"nova-settings-other\"', $navigationView);\n    }\n}\n"
  },
  {
    "path": "tests/Feature/SettingsCastTest.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests\\Feature;\n\nuse Laravel\\Nova\\Fields\\Number;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Outl1ne\\NovaSettings\\Models\\Settings;\nuse Outl1ne\\NovaSettings\\Tests\\IntegrationTestCase;\n\nclass SettingsCastTest extends IntegrationTestCase\n{\n    public function test_integer_casting_works()\n    {\n        NovaSettings::addSettingsFields([\n            Number::make('Test'),\n        ], ['test' => 'int']);\n\n        Settings::create(['key' => 'test', 'value' => '555']);\n\n        $settingValue = nova_get_setting('test');\n        $this->assertIsInt($settingValue);\n        $this->assertEquals(555, $settingValue);\n    }\n\n    public function test_array_casting_works()\n    {\n        NovaSettings::addSettingsFields([\n            Number::make('Test'),\n        ], ['test' => 'array']);\n\n        $testValue = ['et' => 'Eesti', 'ru' => 'Russia'];\n        Settings::create(['key' => 'test', 'value' => $testValue]);\n\n        $settingValue = nova_get_setting('test');\n        $this->assertIsArray($settingValue);\n        $this->assertEquals($testValue, $settingValue);\n    }\n\n    public function test_boolean_casting_works()\n    {\n        NovaSettings::addSettingsFields([\n            Number::make('Test'),\n        ], ['test' => 'boolean']);\n\n        Settings::create(['key' => 'test', 'value' => 1]);\n\n        $settingValue = nova_get_setting('test');\n        $this->assertIsBool($settingValue);\n        $this->assertTrue($settingValue);\n    }\n}\n"
  },
  {
    "path": "tests/Feature/SettingsHelpersTest.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests\\Feature;\n\nuse Outl1ne\\NovaSettings\\Models\\Settings;\nuse Outl1ne\\NovaSettings\\Tests\\IntegrationTestCase;\n\nclass SettingsHelpersTest extends IntegrationTestCase\n{\n    public function test_nova_get_setting_works()\n    {\n        Settings::create(['key' => 'test', 'value' => '555']);\n\n        $this->assertEquals('555', nova_get_setting('test'));\n    }\n\n    public function test_nova_get_settings_works()\n    {\n        Settings::create(['key' => 'test', 'value' => '555']);\n        Settings::create(['key' => 'testtwo', 'value' => '123']);\n\n        $this->assertEquals(['test' => '555', 'testtwo' => '123'], nova_get_settings(['test', 'testtwo']));\n    }\n}\n"
  },
  {
    "path": "tests/Feature/SettingsRetrieveTest.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests\\Feature;\n\nuse Laravel\\Nova\\Fields\\Text;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Outl1ne\\NovaSettings\\Tests\\IntegrationTestCase;\n\nclass SettingsRetrieveTest extends IntegrationTestCase\n{\n    public function test_general_fields_are_returned_with_no_path()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('Test'),\n            Text::make('TestOne'),\n        ]);\n\n        NovaSettings::addSettingsFields([\n            Text::make('TestTwo'),\n            Text::make('TestThree'),\n            Text::make('TestFour'),\n        ], [], 'Other');\n\n        $request = $this->getJson(route('nova-settings.get'));\n\n        $request->assertStatus(200);\n        $request->assertJsonCount(2, 'fields');\n    }\n\n    public function test_general_fields_are_returned_with_general_path()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('Test'),\n            Text::make('TestOne'),\n        ]);\n\n        NovaSettings::addSettingsFields([\n            Text::make('TestTwo'),\n            Text::make('TestThree'),\n            Text::make('TestFour'),\n        ], [], 'Other');\n\n        $request = $this->getJson(route('nova-settings.get', ['path' => 'general']));\n\n        $request->assertStatus(200);\n        $request->assertJsonCount(2, 'fields');\n    }\n\n    public function test_other_fields_are_returned_with_other_path()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('Test'),\n            Text::make('TestOne'),\n        ]);\n\n        NovaSettings::addSettingsFields([\n            Text::make('TestTwo'),\n            Text::make('TestThree'),\n            Text::make('TestFour'),\n        ], [], 'Other');\n\n        $request = $this->getJson(route('nova-settings.get', ['path' => 'other']));\n\n        $request->assertStatus(200);\n        $request->assertJsonCount(3, 'fields');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/SettingsSaveTest.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests\\Feature;\n\nuse Laravel\\Nova\\Fields\\Text;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Outl1ne\\NovaSettings\\Models\\Settings;\nuse Outl1ne\\NovaSettings\\Tests\\IntegrationTestCase;\n\nclass SettingsSaveTest extends IntegrationTestCase\n{\n    public function test_settings_are_saved()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('Test'),\n            Text::make('TestOne'),\n        ]);\n\n        $request = $this->postJson(route('nova-settings.save'), ['test' => 'Test Value']);\n\n        $request->assertStatus(204);\n        $this->assertEquals('Test Value', Settings::getValueForKey('test'));\n    }\n\n    public function test_settings_are_saved_with_path()\n    {\n        NovaSettings::addSettingsFields([\n            Text::make('TestTwo'),\n            Text::make('TestThree'),\n            Text::make('TestFour'),\n        ], [], 'Other');\n\n        $request = $this->postJson(route('nova-settings.save'), ['path' => 'other', 'testthree' => 'Test Value']);\n\n        $request->assertStatus(204);\n        $this->assertEquals('Test Value', Settings::getValueForKey('testthree'));\n    }\n}\n"
  },
  {
    "path": "tests/IntegrationTestCase.php",
    "content": "<?php\n\nnamespace Outl1ne\\NovaSettings\\Tests;\n\nuse Laravel\\Nova\\Nova;\nuse Laravel\\Nova\\NovaServiceProvider;\nuse Illuminate\\Support\\Facades\\Route;\nuse Outl1ne\\NovaSettings\\NovaSettings;\nuse Orchestra\\Testbench\\TestCase as Orchestra;\nuse Outl1ne\\NovaSettings\\NovaSettingsServiceProvider;\n\nabstract class IntegrationTestCase extends Orchestra\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        NovaSettings::clearFields();\n        Route::middlewareGroup('nova', []);\n        Nova::$tools = [\n            new NovaSettings,\n        ];\n\n        $this->setUpDatabase($this->app);\n    }\n\n    protected function getPackageProviders($app)\n    {\n        return [\n            NovaServiceProvider::class,\n            NovaSettingsServiceProvider::class,\n        ];\n    }\n\n    protected function setUpDatabase()\n    {\n        $this->artisan('migrate:fresh');\n    }\n}\n"
  },
  {
    "path": "tests/bootstrap.php",
    "content": "<?php\n\nuse Dotenv\\Dotenv;\nuse Illuminate\\Support\\Env;\n\nrequire __DIR__.'/../vendor/autoload.php';\n\nDotenv::create(\n    Env::getRepository(),\n    __DIR__.'/../',\n    '.env.dusk'\n)->safeLoad();\n\nif (isset($_SERVER['CI']) || isset($_ENV['CI'])) {\n    Orchestra\\Testbench\\Dusk\\Options::withoutUI();\n} else {\n    Orchestra\\Testbench\\Dusk\\Options::withUI();\n}\n"
  },
  {
    "path": "webpack.mix.js",
    "content": "let mix = require('laravel-mix');\nlet path = require('path');\n\nmix.extend('nova', new require('laravel-nova-devtool'));\n\nmix\n  .setPublicPath('dist')\n  .js('resources/js/entry.js', 'js')\n  .vue({ version: 3 })\n  .nova('outl1ne/nova-settings')\n  .alias({\n    'laravel-nova': path.join(__dirname, 'vendor/laravel/nova/resources/js/mixins/packages.js'),\n  });\n"
  }
]