Skip to content

Commit

Permalink
Commander vue component.
Browse files Browse the repository at this point in the history
  • Loading branch information
mtvbrianking committed Jan 30, 2021
1 parent 87155b1 commit 23422df
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*.php diff=php

/.github export-ignore
/images export-ignore
/tests export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"illuminate/contracts": "^6.0|^7.0|^8.0",
"illuminate/http": "^6.0|^7.0|^8.0",
"illuminate/support": "^6.0|^7.0|^8.0",
"symfony/console": "^4.3.4|^5.0"
"symfony/console": "^4.3.4|^5.0",
"symfony/http-kernel": "^4.3.4|^5.0"
},
"require-dev": {
"code-lts/doctum": "^5.3",
Expand Down
Binary file added images/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 31 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
## Laravel Artisan GUI.
# Laravel Artisan GUI.

[![Build Status](https://travis-ci.org/mtvbrianking/laravel-artisan-gui.svg?branch=master)](https://travis-ci.org/mtvbrianking/laravel-artisan-gui)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mtvbrianking/laravel-artisan-gui/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mtvbrianking/laravel-artisan-gui/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/mtvbrianking/laravel-artisan-gui/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/mtvbrianking/laravel-artisan-gui/?branch=master)
[![StyleCI](https://github.styleci.io/repos/230607368/shield?branch=master)](https://github.styleci.io/repos/230607368)
[![Documentation](https://img.shields.io/badge/Documentation-Blue)](https://mtvbrianking.github.io/laravel-artisan-gui)

### [Installation](https://packagist.org/packages/bmatovu/laravel-artisan-gui)
![](./images/banner.png)

## [Installation](https://packagist.org/packages/bmatovu/laravel-artisan-gui)

Install via composer package manager:

```bash
composer require bmatovu/laravel-artisan-gui
```

![](./images/demo.png)

## Usage

> This package uses [Vue](https://vuejs.org) components.. These components also use the Bootstrap CSS framework. However, even if you are not using these tools, the components serve as a valuable reference for your own frontend implementation.
To publish the Vue components, use the `vendor:publish` Artisan command:

```bash
php artisan vendor:publish --tag=artisan-gui-components
```

The published components will be placed in your `resources/js/components` directory. Once the components have been published, you should register them in your `resources/js/app.js` file:

```js
Vue.component(
'artisan-gui-commander',
require('./components/artisan-gui/Commander.vue').default
);
```

After registering the components, make sure to run `npm run dev` to recompile your assets. Once you have recompiled your assets, you may drop the components into one of your application's templates to get started creating clients and personal access tokens:

```html
<artisan-gui-commander/>
```
284 changes: 284 additions & 0 deletions resources/js/components/Commander.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
<template>
<div class="artisan row">
<div class="col-lg-3 col-md-4 col-sm-5">
<div class="card">
<div class="card-body">
<h4>Commands</h4>
<input type="text" v-model="search" class="form-control" placeholder="Search...">
<hr/>
<ul class="namespaces">
<li class="namespace"
v-for="(commands, namespace) in filteredCommands"
v-bind:key="namespace">
{{ namespace }}
<ul class="commands">
<li class="command"
v-for="(commandName, idx) in commands"
v-bind:key="idx"
v-on:click.prevent="select(commandName, $event)"
v-bind:class="{ selected: command && (command.name == commandName) }">
{{ commandName }}
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="col-lg-9 col-md-8 col-sm-7">
<div class="console mb-3" v-if="this.output">
<pre class="command font-weight-bold">>_ php artisan {{ this.command.name }} {{ this.parameters }}</pre>
<pre class="output">{{ this.output }}</pre>
</div>
<div class="card" v-if="this.command">
<div class="card-body">
<form method="POST" @submit.prevent="onSubmit">
<div class="d-flex flex-row justify-content-between">
<div>
<h3>{{ this.command.name }}</h3>
<small class="d-block">{{ this.command.description }}</small>
<!-- <code>{{ this.command.synopsis }}</code> -->
</div>
<div class="d-flex align-items-center">
<button type="submit" class="btn btn-dark">
<i class="bx bx-terminal"></i> Run
</button>
</div>
</div>

<hr>

<h5 v-if="hasArguments">Arguments</h5>

<hr v-if="hasArguments">

<div class="form-group row" v-for="(argument, name) in this.command.arguments" v-bind:key="name">
<label class="col-lg-3 col-md-4 col-form-label" v-bind:class="{ required: argument.is_required }">
{{ name }} <span v-if="argument.is_array">[+]</span>
</label>

<div class="col-lg-9 col-md-8">
<input type="text" class="form-control"
v-if="! argument.is_array"
v-model="argument.value"
:required="argument.is_required"/>

<input type="text" class="form-control"
v-if="argument.is_array"
v-for="(item, index) in argument.value" :key="index"
v-model="argument.value[index]" :required="argument.is_required"/>

<small class="form-text text-muted">{{ argument.description }}</small>
</div>
</div>

<h5 v-if="hasOptions">Options</h5>

<hr v-if="hasOptions">

<div class="form-group row" v-for="(option, name) in this.command.options" v-bind:key="name">
<label class="col-lg-3 col-md-4 col-form-label" :class="{ required: option.is_required }">
{{ name }} <span v-if="option.is_array">[+]</span>
</label>

<div class="col-lg-9 col-md-8" v-if="! option.is_flag">
<input type="text" class="form-control"
v-if="! option.is_array"
v-model="option.value"
:required="option.is_required"/>

<input type="text" class="form-control"
v-if="option.is_array"
v-for="(item, index) in option.value" :key="index"
v-model="option.value[index]" :required="option.is_required"/>

<small class="form-text text-muted">{{ option.description }}</small>
</div>

<div class="col-lg-9 col-md-8" v-if="option.is_flag">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="option.value" :required="option.is_required"/>
<label class="form-check-label" for="">{{ option.description }}</label>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</template>

<script>
export default {
name: 'commander',
data() {
return {
search: '',
command: null,
// arguments: null,
// options: null,
namespaces: null,
commands: null,
parameters: null,
output: null,
}
},
mounted() {
this.initArtisan();
},
methods: {
initArtisan: function() {
// route('commands')
axios.get('/commands')
.then((response) => {
this.namespaces = response.data.namespaces;
// this.arguments = response.data.definition.arguments;
// this.options = response.data.definition.options;
this.commands = response.data.commands;
}, (error) => {
console.error(error);
});
},
select: function(commandName, event) {
// if(event.ctrlKey) { console.log('Ctrl + click'); }
this.output = null;
this.command = this.commands[commandName];
// console.log(JSON.parse(JSON.stringify(this.command)));
if(this.hasArguments) {
for(const name in this.command.arguments) {
if(this.command.arguments[name].is_array) {
this.command.arguments[name].value = [''];
}
}
}
if(this.hasOptions) {
for(const name in this.command.options) {
if(this.command.options[name].is_array) {
this.command.options[name].value = [''];
}
}
}
},
formParams: function() {
var params = {};
for(const name in this.command.arguments) {
params[name] = this.command.arguments[name].value;
}
for(const name in this.command.options) {
if(this.command.options[name].is_flag && ! this.command.options[name].value) {
continue;
}
params[`--${name}`] = this.command.options[name].value;
}
return params;
},
onSubmit: function() {
var params = this.formParams();
// route('commands.execute', this.command.name)
axios.post(`/commands/${this.command.name}`, params)
.then((response) => {
this.output = response.data.output;
this.parameters = response.data.parameters;
// console.log(response.data);
}, (error) => {
console.error(error);
});
}
},
computed: {
hasArguments: function() {
return this.command && Object.keys(this.command.arguments).length;
},
hasOptions: function() {
return this.command && Object.keys(this.command.options).length;
},
filteredCommands: function() {
let app = this;
var matchedNs = {};
for(const group in app.namespaces) {
if (! app.namespaces.hasOwnProperty(group)) { continue; }
var commands = app.namespaces[group];
var matched = commands.filter(function(commandName) {
return commandName.toLowerCase().indexOf(app.search.toLowerCase()) !== -1
});
if(matched.length == 0) {
continue;
}
matchedNs[group] = matched;
}
return matchedNs;
}
}
}
</script>

<style scoped>
ul.namespaces {
height: calc(100vh - 250px);
overflow-y: auto;
}
ul.namespaces > li {
margin: 10px 0;
/*font-weight: bold;*/
/*text-transform: uppercase;*/
}
ul.commands {
list-style: circle;
list-style-type: "";
padding-left: 10px;
}
ul.commands > li {
padding-left: 10px;
}
ul.commands > li:hover {
cursor: pointer;
text-decoration: underline;
}
ul.commands > li.selected {
font-weight: bold;
}
.console {
background: #212529;
padding: 10px;
border-radius: 5px;
/*max-height: 100vh;*/
max-height: 500px;
overflow-y: auto;
}
.console pre {
color: #eee;
}
.console pre.command {
background: #46484a;
padding: 10px;
border-radius: 5px;
white-space: normal;
}
</style>
8 changes: 6 additions & 2 deletions src/ArtisanGuiServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/artisan-gui.php' => base_path('config/artisan-gui.php'),
], 'config');
__DIR__.'/../config/artisan-gui.php' => config_path('artisan-gui.php'),
], 'artisan-gui-config');

$this->publishes([
__DIR__.'/../resources/js/components' => base_path('resources/js/components/artisan-gui'),
], 'artisan-gui-components');
}

$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
Expand Down
Loading

0 comments on commit 23422df

Please sign in to comment.