This commit is contained in:
2024-09-01 18:54:23 +05:00
parent 76d18365a5
commit 061f09eca1
1597 changed files with 109451 additions and 1 deletions

View File

@@ -0,0 +1,74 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'nova:action')]
class ActionCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:action';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new action class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Action';
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
$extension = $this->option('queued') ? 'queued.stub' : 'stub';
if ($this->option('destructive')) {
return $this->resolveStubPath("/stubs/nova/destructive-action.{$extension}");
}
return $this->resolveStubPath("/stubs/nova/action.{$extension}");
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Actions';
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['destructive', null, InputOption::VALUE_NONE, 'Indicate that the action deletes / destroys resources'],
['queued', null, InputOption::VALUE_NONE, 'Indicates the action should be queued'],
];
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:asset')]
class AssetCommand extends ComponentGeneratorCommand
{
use RenamesStubs;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:asset {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new asset';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->hasValidNameArgument()) {
return;
}
(new Filesystem)->copyDirectory(
__DIR__.'/asset-stubs',
$this->componentPath()
);
// AssetServiceProvider.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/AssetServiceProvider.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/AssetServiceProvider.stub');
$this->replace('{{ name }}', $this->componentName(), $this->componentPath().'/src/AssetServiceProvider.stub');
// asset.js replacements...
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/resources/js/asset.js');
$this->replace('{{ name }}', $this->componentName(), $this->componentPath().'/resources/js/asset.js');
// webpack.mix.js replacements...
$this->replace('{{ name }}', $this->component(), $this->componentPath().'/webpack.mix.js');
// Asset composer.json replacements...
$this->prepareComposerReplacements();
// Rename the stubs with the proper file extensions...
$this->renameStubs();
// Register the asset...
$this->buildComponent('asset');
}
/**
* Get the array of stubs that need PHP file extensions.
*
* @return array
*/
protected function stubsToRename()
{
return [
$this->componentPath().'/src/AssetServiceProvider.stub',
];
}
/**
* Get the "title" name of the asset.
*
* @return string
*/
protected function componentTitle()
{
return Str::title(str_replace('-', ' ', $this->componentName()));
}
/**
* Get the component's "snake" name.
*
* @return string
*/
protected function componentSlug()
{
return Str::snake($this->componentName(), '-');
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:base-resource')]
class BaseResourceCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:base-resource';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new base resource class';
/**
* Indicates whether the command should be shown in the Artisan command list.
*
* @var bool
*/
protected $hidden = true;
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Resource';
/**
* Execute the console command.
*
* @return bool|null
*/
public function handle()
{
parent::handle();
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/base-resource.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova';
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:card')]
class CardCommand extends ComponentGeneratorCommand
{
use RenamesStubs;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:card {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new card';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->hasValidNameArgument()) {
return;
}
(new Filesystem)->copyDirectory(
__DIR__.'/card-stubs',
$this->componentPath()
);
// Card.js replacements...
$this->replace('{{ title }}', $this->componentTitle(), $this->componentPath().'/resources/js/components/Card.vue');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/resources/js/card.js');
// Card.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/Card.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/src/Card.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/Card.stub');
(new Filesystem)->move(
$this->componentPath().'/src/Card.stub',
$this->componentPath().'/src/'.$this->componentClass().'.php'
);
// CardServiceProvider.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/CardServiceProvider.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/CardServiceProvider.stub');
$this->replace('{{ name }}', $this->componentName(), $this->componentPath().'/src/CardServiceProvider.stub');
// webpack.mix.js replacements...
$this->replace('{{ name }}', $this->component(), $this->componentPath().'/webpack.mix.js');
// Card composer.json replacements...
$this->prepareComposerReplacements();
// Rename the stubs with the proper file extensions...
$this->renameStubs();
// Register the card...
$this->buildComponent('card');
}
/**
* Get the array of stubs that need PHP file extensions.
*
* @return array
*/
protected function stubsToRename()
{
return [
$this->componentPath().'/src/CardServiceProvider.stub',
$this->componentPath().'/routes/api.stub',
];
}
/**
* Get the "title" name of the card.
*
* @return string
*/
protected function componentTitle()
{
return Str::title(str_replace('-', ' ', $this->componentName()));
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Laravel\Nova\Nova;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:check-license')]
class CheckLicenseCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:check-license';
/** * The console command description.
*
* @var string
*/
protected $description = 'Verify your Nova license key';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
Cache::forget('nova_valid_license_key');
$response = Nova::checkLicense();
if ($response->status() == 204) {
$this->info('Your license key is valid and correctly configured! Thank you for being a Nova customer. 🚀');
return 0;
}
$this->error($response->json('message'));
return 1;
}
}

View File

@@ -0,0 +1,276 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Laravel\Nova\Console\Concerns\AcceptsNameAndVendor;
use Symfony\Component\Process\Process;
abstract class ComponentGeneratorCommand extends Command
{
use AcceptsNameAndVendor, ResolvesStubPath;
/**
* Prepare composer replacements.
*
* @return void
*/
protected function prepareComposerReplacements()
{
$composerJson = $this->componentPath().'/composer.json';
$this->replace('{{ name }}', $this->component(), $composerJson);
$this->replace('{{ escapedNamespace }}', $this->escapedComponentNamespace(), $composerJson);
}
/**
* Register and build the component.
*
* @param string $componentType
* @param bool $interactsWithComposer
* @param bool $interactsWithNpm
* @return void
*/
protected function buildComponent($componentType, $interactsWithComposer = true, $interactsWithNpm = true)
{
if ($interactsWithComposer === true) {
$this->addRepositoryToRootComposer();
$this->addRequireToRootComposer();
if ($this->confirm('Would you like to update your Composer packages?', true)) {
$this->composerUpdate();
$this->output->newLine();
}
}
if ($interactsWithNpm === true) {
if (file_exists(base_path('package.json'))) {
$this->addScriptsToRootNpmPackage();
} else {
$this->warn('Please create a package.json to the root of your project.');
}
if ($this->confirm("Would you like to install the {$componentType}'s NPM dependencies?", true)) {
$this->installNpmDependencies();
$this->output->newLine();
}
if ($this->confirm("Would you like to compile the {$componentType}'s assets?", true)) {
$this->compileAssets();
$this->output->newLine();
}
}
}
/**
* Run the given command as a process.
*
* @param string $command
* @param string $path
* @return void
*/
protected function executeCommand($command, $path)
{
$process = Process::fromShellCommandline($command, $path)->setTimeout(null);
if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
$process->setTty(true);
}
$process->run(function ($type, $line) {
$this->output->write($line);
});
}
/**
* Update the project's composer dependencies.
*
* @return void
*/
protected function composerUpdate()
{
$this->executeCommand('composer update', getcwd());
}
/**
* Add a package entry for the component to the application's composer.json file.
*
* @return void
*/
protected function addRequireToRootComposer()
{
$composer = json_decode(file_get_contents(base_path('composer.json')), true);
$composer['require'][$this->component()] = '@dev';
file_put_contents(
base_path('composer.json'),
json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
}
/**
* Add a path repository for the component to the application's composer.json file.
*
* @return void
*/
protected function addRepositoryToRootComposer()
{
$composer = json_decode(file_get_contents(base_path('composer.json')), true);
$composer['repositories'][] = [
'type' => 'path',
'url' => './'.$this->relativeComponentPath(),
];
file_put_contents(
base_path('composer.json'),
json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
);
}
/**
* Add a path repository for the component to the application's composer.json file.
*
* @return void
*/
protected function addScriptsToRootNpmPackage()
{
$package = json_decode(file_get_contents(base_path('package.json')), true);
$package['scripts']['build-'.$this->componentName()] = 'cd '.$this->relativeComponentPath().' && npm run dev';
$package['scripts']['build-'.$this->componentName().'-prod'] = 'cd '.$this->relativeComponentPath().' && npm run prod';
file_put_contents(
base_path('package.json'),
json_encode($package, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
);
}
/**
* Install the component's NPM dependencies.
*
* @return void
*/
protected function installNpmDependencies()
{
$this->executeCommand('npm set progress=false && npm install', $this->componentPath());
}
/**
* Install the Nova's NPM dependencies.
*
* @return void
*/
protected function installNovaNpmDependencies()
{
$this->executeCommand('npm set progress=false && npm ci', realpath(__DIR__.'/../../'));
}
/**
* Compile the component's assets.
*
* @return void
*/
protected function compileAssets()
{
$this->executeCommand('npm run dev', $this->componentPath());
}
/**
* Replace the given string in the given file.
*
* @param string|array $search
* @param string|array $replace
* @param string $path
* @return void
*/
protected function replace($search, $replace, $path)
{
file_put_contents($path, str_replace($search, $replace, file_get_contents($path)));
}
/**
* Get the path to the component.
*
* @return string
*/
protected function componentPath()
{
return base_path('nova-components/'.$this->componentClass());
}
/**
* Get the relative path to the component.
*
* @return string
*/
protected function relativeComponentPath()
{
return 'nova-components/'.$this->componentClass();
}
/**
* Get the component's namespace.
*
* @return string
*/
protected function componentNamespace()
{
return Str::studly($this->componentVendor()).'\\'.$this->componentClass();
}
/**
* Get the component's escaped namespace.
*
* @return string
*/
protected function escapedComponentNamespace()
{
return str_replace('\\', '\\\\', $this->componentNamespace());
}
/**
* Get the component's class name.
*
* @return string
*/
protected function componentClass()
{
return Str::studly($this->componentName());
}
/**
* Get the component's vendor.
*
* @return string
*/
protected function componentVendor()
{
return explode('/', $this->component())[0];
}
/**
* Get the component's base name.
*
* @return string
*/
protected function componentName()
{
return explode('/', $this->component())[1];
}
/**
* Get the component's name.
*
* @return string
*/
protected function component()
{
return $this->argument('name');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Laravel\Nova\Console\Concerns;
use Illuminate\Support\Str;
trait AcceptsNameAndVendor
{
/**
* Determine if the name argument is valid.
*
* @return bool
*/
public function hasValidNameArgument()
{
$name = $this->argument('name');
if (! Str::contains($name, '/')) {
$this->error("The name argument expects a vendor and name in 'Composer' format. Here's an example: `vendor/name`.");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:custom-filter')]
class CustomFilterCommand extends ComponentGeneratorCommand
{
use RenamesStubs;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:custom-filter {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new custom filter';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->hasValidNameArgument()) {
return;
}
(new Filesystem)->copyDirectory(
__DIR__.'/filter-stubs',
$this->componentPath()
);
// Filter.js replacements...
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/resources/js/filter.js');
// Filter.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/Filter.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/src/Filter.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/Filter.stub');
(new Filesystem)->move(
$this->componentPath().'/src/Filter.stub',
$this->componentPath().'/src/'.$this->componentClass().'.php'
);
// FilterServiceProvider.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/FilterServiceProvider.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/FilterServiceProvider.stub');
// webpack.mix.js replacements...
$this->replace('{{ name }}', $this->component(), $this->componentPath().'/webpack.mix.js');
// Filter composer.json replacements...
$this->prepareComposerReplacements();
// Rename the stubs with the proper file extensions...
$this->renameStubs();
// Register the filter...
$this->buildComponent('filter');
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return __DIR__.'/stubs/filter.stub';
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Filters';
}
/**
* Get the array of stubs that need PHP file extensions.
*
* @return array
*/
protected function stubsToRename()
{
return [
$this->componentPath().'/src/FilterServiceProvider.stub',
];
}
/**
* Get the "title" name of the filter.
*
* @return string
*/
protected function componentTitle()
{
return Str::title(str_replace('-', ' ', $this->componentName()));
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:dashboard')]
class DashboardCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:dashboard {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new dashboard.';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Dashboard';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$stub = str_replace('uri-key', Str::snake($this->argument('name'), '-'), $stub);
return str_replace('dashboard-name', ucwords(Str::snake($this->argument('name'), ' ')), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
if ($this->argument('name') === 'Main') {
return $this->resolveStubPath('/stubs/nova/main-dashboard.stub');
}
return $this->resolveStubPath('/stubs/nova/dashboard.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Dashboards';
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:field')]
class FieldCommand extends ComponentGeneratorCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:field {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new field';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->hasValidNameArgument()) {
return;
}
(new Filesystem)->copyDirectory(
__DIR__.'/field-stubs',
$this->componentPath()
);
// Field.js replacements...
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/resources/js/field.js');
// Field.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/Field.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/src/Field.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/Field.stub');
(new Filesystem)->move(
$this->componentPath().'/src/Field.stub',
$this->componentPath().'/src/'.$this->componentClass().'.php'
);
// FieldServiceProvider.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/FieldServiceProvider.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/FieldServiceProvider.stub');
// webpack.mix.js replacements...
$this->replace('{{ name }}', $this->component(), $this->componentPath().'/webpack.mix.js');
(new Filesystem)->move(
$this->componentPath().'/src/FieldServiceProvider.stub',
$this->componentPath().'/src/FieldServiceProvider.php'
);
// Field composer.json replacements...
$this->prepareComposerReplacements();
// Register the field...
$this->installNovaNpmDependencies();
$this->buildComponent('field');
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'nova:filter')]
class FilterCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:filter';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new filter class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Filter';
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
if ($this->option('boolean')) {
return $this->resolveStubPath('/stubs/nova/boolean-filter.stub');
} elseif ($this->option('date')) {
return $this->resolveStubPath('/stubs/nova/date-filter.stub');
}
return $this->resolveStubPath('/stubs/nova/filter.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Filters';
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['boolean', null, InputOption::VALUE_NONE, 'Indicates if the generated filter should be a boolean filter'],
['date', null, InputOption::VALUE_NONE, 'Indicates if the generated filter should be a date filter'],
];
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Illuminate\Foundation\Configuration\ApplicationBuilder;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:install')]
class InstallCommand extends Command
{
use ResolvesStubPath;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:install';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Install all of the Nova resources';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->comment('Publishing Nova Assets / Resources...');
$this->callSilent('nova:publish');
$this->comment('Publishing Nova Service Provider...');
$this->callSilent('vendor:publish', ['--tag' => 'nova-provider']);
$this->comment('Generating Main Dashboard...');
$this->callSilent('nova:dashboard', ['name' => 'Main']);
copy($this->resolveStubPath('/stubs/nova/main-dashboard.stub'), app_path('Nova/Dashboards/Main.php'));
$this->installNovaServiceProvider();
$this->comment('Generating User Resource...');
$this->callSilent('nova:resource', ['name' => 'User']);
copy($this->resolveStubPath('/stubs/nova/user-resource.stub'), app_path('Nova/User.php'));
if (file_exists(app_path('Models/User.php'))) {
file_put_contents(
app_path('Nova/User.php'),
str_replace(
['App\User::class', 'class-string<\App\User>'],
['App\Models\User::class', 'class-string<\App\Models\User>'],
file_get_contents(app_path('Nova/User.php'))
)
);
}
$this->setAppNamespace();
$this->info('Nova scaffolding installed successfully.');
}
/**
* Install the Nova service providers in the application configuration file.
*
* @return void
*/
protected function installNovaServiceProvider()
{
$namespace = Str::replaceLast('\\', '', $this->laravel->getNamespace());
$appConfig = file_get_contents(config_path('app.php'));
$lineEndingCount = [
"\r\n" => substr_count($appConfig, "\r\n"),
"\r" => substr_count($appConfig, "\r"),
"\n" => substr_count($appConfig, "\n"),
];
$eol = array_keys($lineEndingCount, max($lineEndingCount))[0];
if (class_exists(ApplicationBuilder::class) && is_file(base_path('bootstrap/providers.php'))) {
ServiceProvider::addProviderToBootstrapFile("{$namespace}\\Providers\\NovaServiceProvider");
if (
! $this->laravel['router']->has('login')
&& $this->confirm('Would you like to use the Nova login screen as your application\'s default login screen?', true)
) {
file_put_contents(
app_path('Providers/NovaServiceProvider.php'),
str_replace(
[$eol.' ->withAuthenticationRoutes()'.$eol],
[$eol.' ->withAuthenticationRoutes(default: true)'.$eol],
file_get_contents(app_path('Providers/NovaServiceProvider.php'))
)
);
}
return;
}
if (Str::contains($appConfig, "{$namespace}\\Providers\\NovaServiceProvider::class")) {
return;
}
file_put_contents(config_path('app.php'), str_replace(
"{$namespace}\\Providers\EventServiceProvider::class,".$eol,
"{$namespace}\\Providers\EventServiceProvider::class,".$eol." {$namespace}\Providers\NovaServiceProvider::class,".$eol,
$appConfig
));
}
/**
* Set the proper application namespace on the installed files.
*
* @return void
*/
protected function setAppNamespace()
{
$namespace = $this->laravel->getNamespace();
$this->setAppNamespaceOn(app_path('Nova/User.php'), $namespace);
$this->setAppNamespaceOn(app_path('Providers/NovaServiceProvider.php'), $namespace);
}
/**
* Set the namespace on the given file.
*
* @param string $file
* @param string $namespace
* @return void
*/
protected function setAppNamespaceOn($file, $namespace)
{
file_put_contents($file, str_replace(
'App\\',
$namespace,
file_get_contents($file)
));
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:lens')]
class LensCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:lens';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new lens class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Lens';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$key = preg_replace('/[^a-zA-Z0-9]+/', '', $this->argument('name'));
return str_replace('uri-key', Str::kebab($key), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/lens.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Lenses';
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:partition')]
class PartitionCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:partition';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new metric (partition) class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Metric';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$key = preg_replace('/[^a-zA-Z0-9]+/', '', $this->argument('name'));
return str_replace('uri-key', Str::kebab($key), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/partition.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Metrics';
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:progress')]
class ProgressCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:progress';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new metric (progress) class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Metric';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$key = preg_replace('/[^a-zA-Z0-9]+/', '', $this->argument('name'));
return str_replace('uri-key', Str::kebab($key), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/progress.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Metrics';
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:publish')]
class PublishCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:publish {--force : Overwrite any existing files}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Publish all of the Nova resources';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->call('vendor:publish', [
'--tag' => 'nova-config',
'--force' => $this->option('force'),
]);
$this->call('vendor:publish', [
'--tag' => 'nova-assets',
'--force' => true,
]);
$this->call('vendor:publish', [
'--tag' => 'nova-lang',
'--force' => $this->option('force'),
]);
$this->call('view:clear');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
trait RenamesStubs
{
/**
* Rename the stubs with PHP file extensions.
*
* @return void
*/
protected function renameStubs()
{
foreach ($this->stubsToRename() as $stub) {
(new Filesystem)->move($stub, str_replace('.stub', '.php', $stub));
}
}
/**
* Get the array of stubs that need PHP file extensions.
*
* @return array
*/
abstract protected function stubsToRename();
}

View File

@@ -0,0 +1,129 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'nova:repeatable')]
class RepeatableCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:repeatable';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new repeatable class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Repeatable';
/**
* Execute the console command.
*
* @return bool|null
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function handle()
{
parent::handle();
}
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$resourceName = $this->argument('name');
/** @var string|null $model */
$model = $this->option('model');
$modelNamespace = $this->getModelNamespace();
if (is_null($model)) {
$model = $modelNamespace.str_replace('/', '\\', $resourceName);
} elseif (! Str::startsWith($model, [
$modelNamespace, '\\',
])) {
$model = $modelNamespace.$model;
}
$replace = [
'DummyFullModel' => $model,
'{{ namespacedModel }}' => $model,
'{{namespacedModel}}' => $model,
];
return str_replace(
array_keys($replace), array_values($replace), parent::buildClass($name)
);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
if ($this->option('model')) {
return $this->resolveStubPath('/stubs/nova/repeatable-model.stub');
}
return $this->resolveStubPath('/stubs/nova/repeatable.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Repeater';
}
/**
* Get the default namespace for the class.
*
* @return string
*/
protected function getModelNamespace()
{
$rootNamespace = $this->laravel->getNamespace();
return is_dir(app_path('Models')) ? $rootNamespace.'Models\\' : $rootNamespace;
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['model', 'm', InputOption::VALUE_REQUIRED, 'The model class being represented.'],
];
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Laravel\Nova\Console;
trait ResolvesStubPath
{
/**
* Resolve the fully-qualified path to the stub.
*
* @param string $stub
* @return string
*/
protected function resolveStubPath($stub)
{
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
? $customPath
: __DIR__.str_replace('nova/', '', $stub);
}
}

View File

@@ -0,0 +1,184 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
#[AsCommand(name: 'nova:resource')]
class ResourceCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:resource';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new resource class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Resource';
/**
* A list of resource names which are protected.
*
* @var array
*/
protected $protectedNames = [
'card',
'cards',
'dashboard',
'dashboards',
'metric',
'metrics',
'script',
'scripts',
'search',
'searches',
'style',
'styles',
];
/**
* Execute the console command.
*
* @return bool|null
*/
public function handle()
{
parent::handle();
$this->callSilent('nova:base-resource', [
'name' => 'Resource',
]);
}
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$resourceName = $this->argument('name');
$model = $this->option('model');
$modelNamespace = $this->getModelNamespace();
if (is_null($model)) {
$model = $modelNamespace.str_replace('/', '\\', $resourceName);
} elseif (! Str::startsWith($model, [
$modelNamespace, '\\',
])) {
$model = $modelNamespace.$model;
}
if (in_array(strtolower($resourceName), $this->protectedNames)) {
$this->warn("You *must* override the uriKey method for your {$resourceName} resource.");
}
$replace = [
'DummyFullModel' => $model,
'{{ namespacedModel }}' => $model,
'{{namespacedModel}}' => $model,
];
$result = str_replace(
array_keys($replace), array_values($replace), parent::buildClass($name)
);
$baseResourceClass = $this->getBaseResourceClass();
if (! class_exists($baseResourceClass)) {
$baseResourceClass = 'Laravel\Nova\Resource';
} elseif (! Str::contains($resourceName, '/') && class_exists($baseResourceClass)) {
return $result;
}
$lineEndingCount = [
"\r\n" => substr_count($result, "\r\n"),
"\r" => substr_count($result, "\r"),
"\n" => substr_count($result, "\n"),
];
$eol = array_keys($lineEndingCount, max($lineEndingCount))[0];
return str_replace(
'use Laravel\Nova\Http\Requests\NovaRequest;'.$eol,
'use Laravel\Nova\Http\Requests\NovaRequest;'.$eol."use {$baseResourceClass};".$eol,
$result
);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/resource.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova';
}
/**
* Get the base resource class.
*
* @return class-string
*/
protected function getBaseResourceClass()
{
$rootNamespace = $this->laravel->getNamespace();
return "{$rootNamespace}Nova\Resource";
}
/**
* Get the default namespace for the class.
*
* @return string
*/
protected function getModelNamespace()
{
$rootNamespace = $this->laravel->getNamespace();
return is_dir(app_path('Models')) ? $rootNamespace.'Models\\' : $rootNamespace;
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['model', 'm', InputOption::VALUE_REQUIRED, 'The model class being represented.'],
];
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:resource-tool')]
class ResourceToolCommand extends ComponentGeneratorCommand
{
use RenamesStubs;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:resource-tool {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new resource tool';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->hasValidNameArgument()) {
return;
}
(new Filesystem)->copyDirectory(
__DIR__.'/resource-tool-stubs',
$this->componentPath()
);
// Tool.js replacements...
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/resources/js/tool.js');
// Tool.vue replacements...
$this->replace('{{ title }}', $this->componentTitle(), $this->componentPath().'/resources/js/components/Tool.vue');
// Tool.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/Tool.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/src/Tool.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/Tool.stub');
$this->replace('{{ title }}', $this->componentTitle(), $this->componentPath().'/src/Tool.stub');
(new Filesystem)->move(
$this->componentPath().'/src/Tool.stub',
$this->componentPath().'/src/'.$this->componentClass().'.php'
);
// ToolServiceProvider.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/ToolServiceProvider.stub');
$this->replace('{{ component }}', $this->componentName(), $this->componentPath().'/src/ToolServiceProvider.stub');
$this->replace('{{ name }}', $this->componentName(), $this->componentPath().'/src/ToolServiceProvider.stub');
// webpack.mix.js replacements...
$this->replace('{{ name }}', $this->component(), $this->componentPath().'/webpack.mix.js');
// Tool composer.json replacements...
$this->prepareComposerReplacements();
// Rename the stubs with the proper file extensions...
$this->renameStubs();
// Register the tool...
$this->buildComponent('resource-tool');
}
/**
* Get the array of stubs that need PHP file extensions.
*
* @return array
*/
protected function stubsToRename()
{
return [
$this->componentPath().'/src/ToolServiceProvider.stub',
$this->componentPath().'/routes/api.stub',
];
}
/**
* Get the "title" name of the tool.
*
* @return string
*/
protected function componentTitle()
{
return Str::title(str_replace('-', ' ', $this->componentName()));
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:stubs')]
class StubPublishCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:stubs {--force : Overwrite any existing files}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Publish all stubs that are available for customization';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! is_dir($stubsPath = $this->laravel->basePath('stubs/nova'))) {
(new Filesystem)->makeDirectory($stubsPath, 0755, true);
}
$files = [
__DIR__.'/stubs/action.stub' => $stubsPath.'/action.stub',
__DIR__.'/stubs/action.queued.stub' => $stubsPath.'/action.queued.stub',
__DIR__.'/stubs/base-resource.stub' => $stubsPath.'/base-resource.stub',
__DIR__.'/stubs/boolean-filter.stub' => $stubsPath.'/boolean-filter.stub',
__DIR__.'/stubs/dashboard.stub' => $stubsPath.'/dashboard.stub',
__DIR__.'/stubs/date-filter.stub' => $stubsPath.'/date-filter.stub',
__DIR__.'/stubs/destructive-action.stub' => $stubsPath.'/destructive-action.stub',
__DIR__.'/stubs/destructive-action.queued.stub' => $stubsPath.'/destructive-action.queued.stub',
__DIR__.'/stubs/filter.stub' => $stubsPath.'/filter.stub',
__DIR__.'/stubs/lens.stub' => $stubsPath.'/lens.stub',
__DIR__.'/stubs/main-dashboard.stub' => $stubsPath.'/main-dashboard.stub',
__DIR__.'/stubs/partition.stub' => $stubsPath.'/partition.stub',
__DIR__.'/stubs/resource.stub' => $stubsPath.'/resource.stub',
__DIR__.'/stubs/trend.stub' => $stubsPath.'/trend.stub',
__DIR__.'/stubs/user-resource.stub' => $stubsPath.'/user-resource.stub',
__DIR__.'/stubs/value.stub' => $stubsPath.'/value.stub',
];
foreach ($files as $from => $to) {
if (! file_exists($to) || $this->option('force')) {
file_put_contents($to, file_get_contents($from));
}
}
$this->info('Nova stubs published successfully.');
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:table')]
class TableCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:table';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new metric (table) class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Metric';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$key = preg_replace('/[^a-zA-Z0-9]+/', '', $this->argument('name'));
return str_replace('uri-key', Str::kebab($key), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/table.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Metrics';
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:tool')]
class ToolCommand extends ComponentGeneratorCommand
{
use RenamesStubs;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:tool {name}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new tool';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (! $this->hasValidNameArgument()) {
return;
}
$noInteraction = $this->option('no-interaction');
(new Filesystem)->copyDirectory(
__DIR__.'/tool-stubs',
$this->componentPath()
);
// Route replacements...
$this->replace(['{{ component }}', '{{ name }}'], $this->componentName(), $this->componentPath().'/routes/api.stub');
$this->replace(['{{ component }}', '{{ name }}'], $this->componentName(), $this->componentPath().'/routes/inertia.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/routes/inertia.stub');
// Tool.js replacements...
$this->replace(['{{ component }}', '{{ name }}'], $this->componentName(), $this->componentPath().'/resources/js/tool.js');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/resources/js/tool.js');
// Tool.vue replacements...
$this->replace('{{ title }}', $this->componentTitle(), $this->componentPath().'/resources/js/pages/Tool.vue');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/resources/js/pages/Tool.vue');
// Tool.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/Tool.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/src/Tool.stub');
$this->replace('{{ title }}', $this->componentTitle(), $this->componentPath().'/src/Tool.stub');
$this->replace(['{{ component }}', '{{ name }}'], $this->componentName(), $this->componentPath().'/src/Tool.stub');
(new Filesystem)->move(
$this->componentPath().'/src/Tool.stub',
$this->componentPath().'/src/'.$this->componentClass().'.php'
);
// ToolServiceProvider.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/ToolServiceProvider.stub');
$this->replace(['{{ component }}', '{{ name }}'], $this->componentName(), $this->componentPath().'/src/ToolServiceProvider.stub');
// webpack.mix.js replacements...
$this->replace('{{ name }}', $this->component(), $this->componentPath().'/webpack.mix.js');
// Authorize.php replacements...
$this->replace('{{ namespace }}', $this->componentNamespace(), $this->componentPath().'/src/Http/Middleware/Authorize.stub');
$this->replace('{{ class }}', $this->componentClass(), $this->componentPath().'/src/Http/Middleware/Authorize.stub');
// Tool composer.json replacements...
$this->prepareComposerReplacements();
// Rename the stubs with the proper file extensions...
$this->renameStubs();
// Register the tool...
$this->buildComponent('tool');
}
/**
* Get the array of stubs that need PHP file extensions.
*
* @return array
*/
protected function stubsToRename()
{
return [
$this->componentPath().'/src/ToolServiceProvider.stub',
$this->componentPath().'/src/Http/Middleware/Authorize.stub',
$this->componentPath().'/routes/api.stub',
$this->componentPath().'/routes/inertia.stub',
];
}
/**
* Get the "title" name of the tool.
*
* @return string
*/
protected function componentTitle()
{
return Str::title(str_replace('-', ' ', $this->componentName()));
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:translate')]
class TranslateCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:translate
{language}
{--force : Overwrite any existing files}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create translation files for Nova';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$language = $this->argument('language');
$jsonLanguageFile = lang_path("vendor/nova/{$language}.json");
if (! File::exists($jsonLanguageFile) || $this->option('force')) {
File::copy(__DIR__.'/../../resources/lang/en.json', $jsonLanguageFile);
}
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:trend')]
class TrendCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:trend';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new metric (trend) class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Metric';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$key = preg_replace('/[^a-zA-Z0-9]+/', '', $this->argument('name'));
return str_replace('uri-key', Str::kebab($key), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/trend.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Metrics';
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:upgrade')]
class UpgradeCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:upgrade';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Upgrade Laravel Nova 3 to 4';
/**
* Execute the console command.
*
* @return bool|null
*/
public function handle()
{
// 1. Prepare Main Dashboard.
$this->call('nova:dashboard', ['name' => 'Main']);
// 2. Publish assets
$this->call('vendor:publish', [
'--tag' => 'nova-assets',
'--force' => true,
]);
// 3. Replace nova config file
if ($this->confirm('Backup existing `nova.php` configuration file?')) {
$this->backupFiles([
config_path('nova.php'),
]);
}
$this->call('vendor:publish', [
'--tag' => 'nova-config',
'--force' => true,
]);
$path = $this->laravel['config']->get('nova.path', '/');
$this->replace("'path' => '/nova',", "'path' => '{$path}',", config_path('nova.php'));
// 4. Replace nova language files
if ($this->confirm('Backup existing `en.json` language file?')) {
$this->backupFiles([
lang_path('vendor/nova/en.json'),
]);
}
$this->call('vendor:publish', [
'--tag' => 'nova-lang',
'--force' => true,
]);
// 5. Delete Nova 3 layout.blade.php if available.
$this->backupFiles([
resource_path('views/vendor/nova/layout.blade.php'),
], true);
// 6. Clear view caches
$this->call('view:clear');
}
/**
* Create backup to the files.
*
* @param array<int, string> $files
* @param bool $removeOriginal
* @return void
*/
protected function backupFiles(array $files, $removeOriginal = false)
{
collect($files)->each(function ($file) use ($removeOriginal) {
if (File::exists($file)) {
File::copy($file, "{$file}.backup");
if ($removeOriginal === true) {
File::delete($file);
}
}
});
}
/**
* Replace the given string in the given file.
*
* @param string|array $search
* @param string|array $replace
* @param string $path
* @return void
*/
protected function replace($search, $replace, $path)
{
file_put_contents($path, str_replace($search, $replace, file_get_contents($path)));
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\Command;
use Laravel\Nova\Nova;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:user')]
class UserCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'nova:user';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new user';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
Nova::createUser($this);
$this->info('User created successfully.');
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Laravel\Nova\Console;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'nova:value')]
class ValueCommand extends GeneratorCommand
{
use ResolvesStubPath;
/**
* The console command name.
*
* @var string
*/
protected $name = 'nova:value';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new metric (single value) class';
/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Metric';
/**
* Build the class with the given name.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$stub = parent::buildClass($name);
$key = preg_replace('/[^a-zA-Z0-9]+/', '', $this->argument('name'));
return str_replace('uri-key', Str::kebab($key), $stub);
}
/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return $this->resolveStubPath('/stubs/nova/value.stub');
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Nova\Metrics';
}
}

10
nova/src/Console/asset-stubs/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "{{ name }}",
"description": "A Laravel Nova asset.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"{{ escapedNamespace }}\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"{{ escapedNamespace }}\\AssetServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,24 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,20 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"laravel-mix": "^6.0.41",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1 @@
/* Nova Asset CSS */

View File

@@ -0,0 +1,5 @@
// import {{ class }} from './components/{{ class }}'
// Nova.booting(app => {
// app.component('{{ name }}', {{ class }})
// })

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class AssetServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Nova::serving(function (ServingNova $event) {
Nova::script('{{ component }}', __DIR__.'/../dist/js/asset.js');
Nova::style('{{ component }}', __DIR__.'/../dist/css/asset.css');
});
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,14 @@
let mix = require('laravel-mix')
let path = require('path')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/asset.js', 'js')
.vue({ version: 3 })
.css('resources/css/asset.css', 'css')
.alias({
'@': path.join(__dirname, 'resources/js/'),
})
.nova('{{ name }}')

10
nova/src/Console/card-stubs/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "{{ name }}",
"description": "A Laravel Nova card.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"{{ escapedNamespace }}\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"{{ escapedNamespace }}\\CardServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,33 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,20 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"laravel-mix": "^6.0.41",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1 @@
/* Nova Card CSS */

View File

@@ -0,0 +1,5 @@
import Card from './components/Card'
Nova.booting((app, store) => {
app.component('{{ component }}', Card)
})

View File

@@ -0,0 +1,24 @@
<template>
<Card class="flex flex-col items-center justify-center">
<div class="px-3 py-3">
<h1 class="text-center text-3xl text-gray-500 font-light">{{ title }}</h1>
</div>
</Card>
</template>
<script>
export default {
props: [
'card',
// The following props are only available on resource detail cards...
// 'resource',
// 'resourceId',
// 'resourceName',
],
mounted() {
//
},
}
</script>

View File

@@ -0,0 +1,19 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Card API Routes
|--------------------------------------------------------------------------
|
| Here is where you may register API routes for your card. These routes
| are loaded by the ServiceProvider of your card. You're free to add
| as many additional routes to this file as your card may require.
|
*/
// Route::get('/endpoint', function (Request $request) {
// //
// });

View File

@@ -0,0 +1,25 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Card;
class {{ class }} extends Card
{
/**
* The width of the card (1/3, 1/2, or full).
*
* @var string
*/
public $width = '1/3';
/**
* Get the component name for the element.
*
* @return string
*/
public function component()
{
return '{{ component }}';
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class CardServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->app->booted(function () {
$this->routes();
});
Nova::serving(function (ServingNova $event) {
Nova::script('{{ component }}', __DIR__.'/../dist/js/card.js');
Nova::style('{{ component }}', __DIR__.'/../dist/css/card.css');
});
}
/**
* Register the card's routes.
*
* @return void
*/
protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}
Route::middleware(['nova'])
->prefix('nova-vendor/{{ name }}')
->group(__DIR__.'/../routes/api.php');
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,10 @@
let mix = require('laravel-mix')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/card.js', 'js')
.vue({ version: 3 })
.css('resources/css/card.css', 'css')
.nova('{{ name }}')

10
nova/src/Console/field-stubs/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "{{ name }}",
"description": "A Laravel Nova field.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"{{ escapedNamespace }}\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"{{ escapedNamespace }}\\FieldServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,40 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackPlugins() {
return new webpack.ProvidePlugin({
_: 'lodash',
Errors: 'form-backend-validation',
})
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,22 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"form-backend-validation": "^2.3.3",
"laravel-mix": "^6.0.41",
"lodash": "^4.17.21",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1 @@
/* Nova Field CSS */

View File

@@ -0,0 +1,9 @@
<template>
<PanelItem :index="index" :field="field" />
</template>
<script>
export default {
props: ['index', 'resource', 'resourceName', 'resourceId', 'field'],
}
</script>

View File

@@ -0,0 +1,45 @@
<template>
<DefaultField
:field="field"
:errors="errors"
:show-help-text="showHelpText"
:full-width-content="fullWidthContent"
>
<template #field>
<input
:id="field.attribute"
type="text"
class="w-full form-control form-input form-control-bordered"
:class="errorClasses"
:placeholder="field.name"
v-model="value"
/>
</template>
</DefaultField>
</template>
<script>
import { FormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
formData.append(this.fieldAttribute, this.value || '')
},
},
}
</script>

View File

@@ -0,0 +1,15 @@
<template>
<span>{{ fieldValue }}</span>
</template>
<script>
export default {
props: ['resourceName', 'field'],
computed: {
fieldValue() {
return this.field.displayedAs || this.field.value
},
}
}
</script>

View File

@@ -0,0 +1,9 @@
import IndexField from './components/IndexField'
import DetailField from './components/DetailField'
import FormField from './components/FormField'
Nova.booting((app, store) => {
app.component('index-{{ component }}', IndexField)
app.component('detail-{{ component }}', DetailField)
app.component('form-{{ component }}', FormField)
})

View File

@@ -0,0 +1,15 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Fields\Field;
class {{ class }} extends Field
{
/**
* The field's component.
*
* @var string
*/
public $component = '{{ component }}';
}

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class FieldServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Nova::serving(function (ServingNova $event) {
Nova::script('{{ component }}', __DIR__.'/../dist/js/field.js');
Nova::style('{{ component }}', __DIR__.'/../dist/css/field.css');
});
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,10 @@
let mix = require('laravel-mix')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/field.js', 'js')
.vue({ version: 3 })
.css('resources/css/field.css', 'css')
.nova('{{ name }}')

View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "{{ name }}",
"description": "A Laravel Nova filter.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"{{ escapedNamespace }}\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"{{ escapedNamespace }}\\FilterServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,33 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,20 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"laravel-mix": "^6.0.41",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1 @@
/* Nova Filter CSS */

View File

@@ -0,0 +1,83 @@
<template>
<FilterContainer>
<span>{{ filter.name }}</span>
<template #filter>
<SelectControl
:dusk="`${filter.name}-select-filter`"
label="label"
class="w-full block"
size="sm"
@change="handleChange"
:value="value"
:options="filter.options"
>
<option value="" :selected="value === ''">&mdash;</option>
</SelectControl>
</template>
</FilterContainer>
</template>
<script>
import debounce from 'lodash/debounce'
export default {
emits: ['change'],
props: {
resourceName: { type: String, required: true },
filterKey: { type: String, required: true },
lens: String,
},
data: () => ({
value: null,
debouncedEmit: null,
}),
created() {
this.debouncedEmit = debounce(() => this.emitChange(), 500)
this.setCurrentFilterValue()
},
mounted() {
Nova.$on('filter-reset', this.setCurrentFilterValue)
},
beforeUnmount() {
Nova.$off('filter-reset', this.setCurrentFilterValue)
},
watch: {
value() {
this.debouncedHandleChange()
},
},
methods: {
setCurrentFilterValue() {
this.value = this.filter.currentValue
},
handleChange(e) {
this.value = e
this.debouncedEmit()
},
emitChange() {
this.$emit('change', {
filterClass: this.filterKey,
value: this.value,
})
},
},
computed: {
filter() {
return this.$store.getters[`${this.resourceName}/getFilter`](
this.filterKey
)
},
},
}
</script>

View File

@@ -0,0 +1,5 @@
import Filter from './components/Filter'
Nova.booting((app, store) => {
app.component('{{ component }}', Filter)
})

View File

@@ -0,0 +1,40 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Filters\Filter;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends Filter
{
/**
* The filter's component.
*
* @var string
*/
public $component = '{{ component }}';
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
return $query;
}
/**
* Get the filter's available options.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function options(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class FilterServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Nova::serving(function (ServingNova $event) {
Nova::script('{{ component }}', __DIR__.'/../dist/js/filter.js');
Nova::style('{{ component }}', __DIR__.'/../dist/css/filter.css');
});
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,10 @@
let mix = require('laravel-mix')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/filter.js', 'js')
.vue({ version: 3 })
.css('resources/css/filter.css', 'css')
.nova('{{ name }}')

View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "{{ name }}",
"description": "A Laravel Nova resource tool.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"{{ escapedNamespace }}\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"{{ escapedNamespace }}\\ToolServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,33 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,20 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"laravel-mix": "^6.0.41",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1 @@
/* Nova Resource Tool CSS */

View File

@@ -0,0 +1,13 @@
<template>
<div>{{ title }}</div>
</template>
<script>
export default {
props: ['resourceName', 'resourceId', 'panel'],
mounted() {
//
},
}
</script>

View File

@@ -0,0 +1,5 @@
import Tool from './components/Tool'
Nova.booting((app, store) => {
app.component('{{ component }}', Tool)
})

View File

@@ -0,0 +1,19 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Tool API Routes
|--------------------------------------------------------------------------
|
| Here is where you may register API routes for your tool. These routes
| are loaded by the ServiceProvider of your tool. You're free to add
| as many additional routes to this file as your tool may require.
|
*/
// Route::get('/endpoint', function (Request $request) {
// //
// });

View File

@@ -0,0 +1,28 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\ResourceTool;
class {{ class }} extends ResourceTool
{
/**
* Get the displayable name of the resource tool.
*
* @return string
*/
public function name()
{
return '{{ title }}';
}
/**
* Get the component name for the resource tool.
*
* @return string
*/
public function component()
{
return '{{ component }}';
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class ToolServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->app->booted(function () {
$this->routes();
});
Nova::serving(function (ServingNova $event) {
Nova::script('{{ component }}', __DIR__.'/../dist/js/tool.js');
Nova::style('{{ component }}', __DIR__.'/../dist/css/tool.css');
});
}
/**
* Register the tool's routes.
*
* @return void
*/
protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}
Route::middleware(['nova'])
->prefix('nova-vendor/{{ name }}')
->group(__DIR__.'/../routes/api.php');
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,10 @@
let mix = require('laravel-mix')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/tool.js', 'js')
.vue({ version: 3 })
.css('resources/css/tool.css', 'css')
.nova('{{ name }}')

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Nova\Nova;
use Laravel\Nova\NovaApplicationServiceProvider;
class NovaServiceProvider extends NovaApplicationServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Register the Nova routes.
*
* @return void
*/
protected function routes()
{
Nova::routes()
->withAuthenticationRoutes()
->withPasswordResetRoutes()
->register();
}
/**
* Register the Nova gate.
*
* This gate determines who can access Nova in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewNova', function ($user) {
return in_array($user->email, [
//
]);
});
}
/**
* Get the dashboards that should be listed in the Nova sidebar.
*
* @return array
*/
protected function dashboards()
{
return [
new \App\Nova\Dashboards\Main,
];
}
/**
* Get the tools that should be listed in the Nova sidebar.
*
* @return array
*/
public function tools()
{
return [];
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Batchable;
use Illuminate\Bus\PendingBatch;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Contracts\BatchableAction;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends Action implements ShouldQueue
{
use Batchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
//
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
/**
* Register `then`, `catch` and `finally` event on batchable job.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Bus\PendingBatch $batch
* @return void
*/
public function withBatch(ActionFields $fields, PendingBatch $batch)
{
//
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends Action
{
use InteractsWithQueue, Queueable;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
//
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Resource as NovaResource;
abstract class Resource extends NovaResource
{
/**
* Build an "index" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function indexQuery(NovaRequest $request, $query)
{
return $query;
}
/**
* Build a Scout search query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Scout\Builder $query
* @return \Laravel\Scout\Builder
*/
public static function scoutQuery(NovaRequest $request, $query)
{
return $query;
}
/**
* Build a "detail" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function detailQuery(NovaRequest $request, $query)
{
return parent::detailQuery($request, $query);
}
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableQuery(NovaRequest $request, $query)
{
return parent::relatableQuery($request, $query);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Filters\BooleanFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends BooleanFilter
{
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
return $query;
}
/**
* Get the filter's available options.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function options(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Dashboard;
class {{ class }} extends Dashboard
{
/**
* Get the cards for the dashboard.
*
* @return array
*/
public function cards()
{
return [
//
];
}
/**
* Get the URI key for the dashboard.
*
* @return string
*/
public function uriKey()
{
return 'uri-key';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\Carbon;
use Laravel\Nova\Filters\DateFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends DateFilter
{
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
$value = Carbon::parse($value);
return $query;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace {{ namespace }};
use Illuminate\Bus\Batchable;
use Illuminate\Bus\PendingBatch;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\DestructiveAction;
use Laravel\Nova\Contracts\BatchableAction;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends DestructiveAction implements ShouldQueue
{
use Batchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
//
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
/**
* Register `then`, `catch` and `finally` event on batchable job.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Bus\PendingBatch $batch
* @return void
*/
public function withBatch(ActionFields $fields, PendingBatch $batch)
{
//
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace {{ namespace }};
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\DestructiveAction;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends DestructiveAction
{
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
//
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Filters\Filter;
use Laravel\Nova\Http\Requests\NovaRequest;
class {{ class }} extends Filter
{
/**
* The filter's component.
*
* @var string
*/
public $component = 'select-filter';
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
return $query;
}
/**
* Get the filter's available options.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function options(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\LensRequest;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Lenses\Lens;
use Laravel\Nova\Nova;
class {{ class }} extends Lens
{
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [];
/**
* Get the query builder / paginator for the lens.
*
* @param \Laravel\Nova\Http\Requests\LensRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return mixed
*/
public static function query(LensRequest $request, $query)
{
return $request->withOrdering($request->withFilters(
$query
));
}
/**
* Get the fields available to the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make(Nova::__('ID'), 'id')->sortable(),
];
}
/**
* Get the cards available on the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the actions available on the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return parent::actions($request);
}
/**
* Get the URI key for the lens.
*
* @return string
*/
public function uriKey()
{
return 'uri-key';
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Nova\Dashboards;
use Laravel\Nova\Cards\Help;
use Laravel\Nova\Dashboards\Main as Dashboard;
class Main extends Dashboard
{
/**
* Get the cards for the dashboard.
*
* @return array
*/
public function cards()
{
return [
new Help,
];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Partition;
class {{ class }} extends Partition
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, Model::class, 'groupByColumn');
}
/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int|null
*/
public function cacheFor()
{
// return now()->addMinutes(5);
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'uri-key';
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace {{ namespace }};
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Progress;
class {{ class }} extends Progress
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, Model::class, function ($query) {
return $query;
}, target: 100);
}
/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int
*/
public function cacheFor()
{
// return now()->addMinutes(5);
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'uri-key';
}
}

Some files were not shown because too many files have changed in this diff Show More