new server create page w.i.p
This commit is contained in:
parent
75622d3958
commit
16a7d174e9
16 changed files with 14269 additions and 233 deletions
59
app/Http/Controllers/ProductController.php
Normal file
59
app/Http/Controllers/ProductController.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Egg;
|
||||
use App\Models\Node;
|
||||
use App\Models\Product;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
/**
|
||||
* @description get product locations based on selected egg
|
||||
* @param Request $request
|
||||
* @param Egg $egg
|
||||
* @return Collection|JsonResponse
|
||||
*/
|
||||
public function getNodesBasedOnEgg(Request $request, Egg $egg)
|
||||
{
|
||||
if (is_null($egg->id)) return response()->json('egg id is required', '400');
|
||||
|
||||
#get products that include this egg
|
||||
$products = Product::query()->with('nodes')->whereHas('eggs', function (Builder $builder) use ($egg) {
|
||||
$builder->where('id', '=', $egg->id);
|
||||
})->get();
|
||||
|
||||
$nodes = collect();
|
||||
|
||||
#filter unique nodes
|
||||
$products->each(function (Product $product) use ($nodes) {
|
||||
$product->nodes->each(function (Node $node) use ($nodes) {
|
||||
if (!$nodes->contains('id', $node->id) && !$node->disabled) {
|
||||
$nodes->add($node);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return Collection|JsonResponse
|
||||
*/
|
||||
public function getProductsBasedOnNode(Node $node)
|
||||
{
|
||||
if (is_null($node->id)) return response()->json('node id is required', '400');
|
||||
|
||||
return Product::query()->whereHas('nodes', function (Builder $builder) use ($node) {
|
||||
$builder->where('id' , '=' , $node->id);
|
||||
})->get();
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ namespace App\Http\Controllers;
|
|||
use App\Classes\Pterodactyl;
|
||||
use App\Models\Configuration;
|
||||
use App\Models\Egg;
|
||||
use App\Models\Location;
|
||||
use App\Models\Nest;
|
||||
use App\Models\Node;
|
||||
use App\Models\Product;
|
||||
|
@ -16,7 +15,6 @@ use Illuminate\Http\Client\Response;
|
|||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Request as FacadesRequest;
|
||||
|
||||
class ServerController extends Controller
|
||||
|
@ -35,56 +33,14 @@ class ServerController extends Controller
|
|||
if (!is_null($this->validateConfigurationRules())) return $this->validateConfigurationRules();
|
||||
|
||||
return view('servers.create')->with([
|
||||
'products' => Product::where('disabled', '=', false)->orderBy('price', 'asc')->get(),
|
||||
'locations' => Location::whereHas('nodes', function ($query) {
|
||||
$query->where('disabled', '=', false);
|
||||
})->get(),
|
||||
'nests' => Nest::where('disabled', '=', false)->get(),
|
||||
'productCount' => Product::query()->where('disabled', '=', false)->count(),
|
||||
'nodeCount' => Node::query()->where('disabled', '=', false)->count(),
|
||||
'nests' => Nest::query()->where('disabled', '=', false)->get(),
|
||||
'eggs' => Egg::query()->where('disabled', '=', false)->get(),
|
||||
'minimum_credits' => Configuration::getValueByKey('MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50)
|
||||
]);
|
||||
}
|
||||
|
||||
/** Store a newly created resource in storage. */
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (!is_null($this->validateConfigurationRules())) return $this->validateConfigurationRules();
|
||||
|
||||
$request->validate([
|
||||
"name" => "required|max:191",
|
||||
"description" => "nullable|max:191",
|
||||
"node_id" => "required|exists:nodes,id",
|
||||
"egg_id" => "required|exists:eggs,id",
|
||||
"product_id" => "required|exists:products,id"
|
||||
]);
|
||||
|
||||
//get required resources
|
||||
$egg = Egg::findOrFail($request->input('egg_id'));
|
||||
$node = Node::findOrFail($request->input('node_id'));
|
||||
$server = Auth::user()->servers()->create($request->all());
|
||||
|
||||
//get free allocation ID
|
||||
$allocationId = Pterodactyl::getFreeAllocationId($node);
|
||||
if (!$allocationId) return $this->noAllocationsError($server);
|
||||
|
||||
//create server on pterodactyl
|
||||
$response = Pterodactyl::createServer($server, $egg, $allocationId);
|
||||
if ($response->failed()) return $this->serverCreationFailed($response, $server);
|
||||
|
||||
//update server with pterodactyl_id
|
||||
$server->update([
|
||||
'pterodactyl_id' => $response->json()['attributes']['id'],
|
||||
'identifier' => $response->json()['attributes']['identifier']
|
||||
]);
|
||||
|
||||
if (Configuration::getValueByKey('SERVER_CREATE_CHARGE_FIRST_HOUR', 'true') == 'true') {
|
||||
if (Auth::user()->credits >= $server->product->getHourlyPrice()) {
|
||||
Auth::user()->decrement('credits', $server->product->getHourlyPrice());
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('servers.index')->with('success', 'server created');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|RedirectResponse
|
||||
*/
|
||||
|
@ -121,17 +77,46 @@ class ServerController extends Controller
|
|||
return null;
|
||||
}
|
||||
|
||||
/** Remove the specified resource from storage. */
|
||||
public function destroy(Server $server)
|
||||
/** Store a newly created resource in storage. */
|
||||
public function store(Request $request)
|
||||
{
|
||||
try {
|
||||
$server->delete();
|
||||
return redirect()->route('servers.index')->with('success', 'server removed');
|
||||
} catch (Exception $e) {
|
||||
return redirect()->route('servers.index')->with('error', 'An exception has occurred while trying to remove a resource "' . $e->getMessage() . '"');
|
||||
}
|
||||
}
|
||||
if (!is_null($this->validateConfigurationRules())) return $this->validateConfigurationRules();
|
||||
|
||||
$request->validate([
|
||||
"name" => "required|max:191",
|
||||
"description" => "nullable|max:191",
|
||||
"node_id" => "required|exists:nodes,id",
|
||||
"egg_id" => "required|exists:eggs,id",
|
||||
"product_id" => "required|exists:products,id"
|
||||
]);
|
||||
|
||||
//get required resources
|
||||
$egg = Egg::findOrFail($request->input('egg_id'));
|
||||
$node = Node::findOrFail($request->input('node_id'));
|
||||
$server = Auth::user()->servers()->create($request->all());
|
||||
|
||||
//get free allocation ID
|
||||
$allocationId = Pterodactyl::getFreeAllocationId($node);
|
||||
if (!$allocationId) return $this->noAllocationsError($server);
|
||||
|
||||
//create server on pterodactyl
|
||||
$response = Pterodactyl::createServer($server, $egg, $allocationId);
|
||||
if ($response->failed()) return $this->serverCreationFailed($response, $server);
|
||||
|
||||
//update server with pterodactyl_id
|
||||
$server->update([
|
||||
'pterodactyl_id' => $response->json()['attributes']['id'],
|
||||
'identifier' => $response->json()['attributes']['identifier']
|
||||
]);
|
||||
|
||||
if (Configuration::getValueByKey('SERVER_CREATE_CHARGE_FIRST_HOUR', 'true') == 'true') {
|
||||
if (Auth::user()->credits >= $server->product->getHourlyPrice()) {
|
||||
Auth::user()->decrement('credits', $server->product->getHourlyPrice());
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('servers.index')->with('success', 'server created');
|
||||
}
|
||||
|
||||
/**
|
||||
* return redirect with error
|
||||
|
@ -158,4 +143,15 @@ class ServerController extends Controller
|
|||
|
||||
return redirect()->route('servers.index')->with('error', json_encode($response->json()));
|
||||
}
|
||||
|
||||
/** Remove the specified resource from storage. */
|
||||
public function destroy(Server $server)
|
||||
{
|
||||
try {
|
||||
$server->delete();
|
||||
return redirect()->route('servers.index')->with('success', 'server removed');
|
||||
} catch (Exception $e) {
|
||||
return redirect()->route('servers.index')->with('error', 'An exception has occurred while trying to remove a resource "' . $e->getMessage() . '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
|
||||
class Egg extends Model
|
||||
{
|
||||
|
@ -85,5 +86,4 @@ class Egg extends Model
|
|||
{
|
||||
return $this->belongsToMany(Product::class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ class Location extends Model
|
|||
|
||||
public $guarded = [];
|
||||
|
||||
|
||||
public function nodes(){
|
||||
return $this->hasMany(Node::class , 'location_id' , 'id');
|
||||
}
|
||||
|
@ -39,4 +38,5 @@ class Location extends Model
|
|||
self::firstOrCreate(['id' => $location['id']] , $location);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
|||
|
||||
class Product extends Model
|
||||
{
|
||||
use HasFactory, LogsActivity;
|
||||
use HasFactory;
|
||||
use LogsActivity;
|
||||
|
||||
public $incrementing = false;
|
||||
|
||||
|
|
13843
package-lock.json
generated
13843
package-lock.json
generated
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
2
public/js/app.js
vendored
2
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5
resources/js/alpine.js
vendored
Normal file
5
resources/js/alpine.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
resources/js/app.js
vendored
1
resources/js/app.js
vendored
|
@ -3,3 +3,4 @@ require('./slim.kickstart.min')
|
|||
require('./bootstrap');
|
||||
|
||||
|
||||
|
||||
|
|
8
resources/js/bootstrap.js
vendored
8
resources/js/bootstrap.js
vendored
|
@ -18,10 +18,10 @@ try {
|
|||
* to our Laravel back-end. This library automatically handles sending the
|
||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||
*/
|
||||
//
|
||||
// window.axios = require('axios');
|
||||
//
|
||||
// window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<li class="breadcrumb-item"><a href="{{ route('home') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.products.index') }}">Products</a></li>
|
||||
<li class="breadcrumb-item"><a class="text-muted"
|
||||
href="{{ route('admin.products.create') }}">Create</a>
|
||||
href="{{ route('admin.products.create') }}">Create</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
@ -54,9 +54,9 @@
|
|||
class="form-control @error('name') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('name')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -67,9 +67,9 @@
|
|||
class="form-control @error('price') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('price')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -81,9 +81,9 @@
|
|||
class="form-control @error('memory') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('memory')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -94,9 +94,9 @@
|
|||
class="form-control @error('cpu') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('cpu')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -107,9 +107,9 @@
|
|||
class="form-control @error('swap') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('swap')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -123,9 +123,9 @@
|
|||
class="form-control @error('description') is-invalid @enderror"
|
||||
required="required">{{$product->description ?? old('description')}}</textarea>
|
||||
@error('description')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -138,9 +138,9 @@
|
|||
class="form-control @error('disk') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('disk')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -150,13 +150,13 @@
|
|||
data-content="Setting to -1 will use the value from configuration."
|
||||
class="fas fa-info-circle"></i></label>
|
||||
<input value="{{ old('minimum_credits') ?? -1 }}" id="minimum_credits"
|
||||
name="minimum_credits" type="number"
|
||||
class="form-control @error('minimum_credits') is-invalid @enderror"
|
||||
required="required">
|
||||
name="minimum_credits" type="number"
|
||||
class="form-control @error('minimum_credits') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('minimum_credits')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
@ -167,9 +167,9 @@
|
|||
class="form-control @error('io') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('io')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -180,9 +180,9 @@
|
|||
class="form-control @error('databases') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('databases')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -193,9 +193,9 @@
|
|||
class="form-control @error('backups') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('backups')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -206,9 +206,9 @@
|
|||
class="form-control @error('allocations') is-invalid @enderror"
|
||||
required="required">
|
||||
@error('allocations')
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
@ -292,14 +292,9 @@
|
|||
<!-- END CONTENT -->
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
$('.custom-select').select2();
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', (event) => {
|
||||
})
|
||||
</script>
|
||||
$('.custom-select').select2();
|
||||
|
||||
@endsection
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
href="{{\Illuminate\Support\Facades\Storage::disk('public')->exists('favicon.ico') ? asset('storage/favicon.ico') : asset('favicon.ico')}}"
|
||||
type="image/x-icon">
|
||||
|
||||
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
|
||||
{{-- <link rel="stylesheet" href="{{asset('css/adminlte.min.css')}}">--}}
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.10.24/datatables.min.css"/>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<li class="breadcrumb-item"><a href="{{ route('home') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('servers.index') }}">Servers</a>
|
||||
<li class="breadcrumb-item"><a class="text-muted"
|
||||
href="{{ route('servers.create') }}">Create</a>
|
||||
href="{{ route('servers.create') }}">Create</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
@ -23,130 +23,291 @@
|
|||
<!-- END CONTENT HEADER -->
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<section x-data="serverApp()" class="content">
|
||||
<div class="container">
|
||||
|
||||
<!-- CUSTOM CONTENT -->
|
||||
<div class="row justify-content-center">
|
||||
<div class="card col-lg-8 col-md-12 mb-5">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title"><i class="fa fa-server mr-2"></i>Create Server</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('servers.store') }}">
|
||||
<!-- FORM -->
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">{{__('Server configuration')}}</div>
|
||||
</div>
|
||||
|
||||
@if($productCount === 0 || $nodeCount === 0 || count($nests) === 0 || count($eggs) === 0 )
|
||||
<div class="alert alert-danger p-2 m-2">
|
||||
<h5><i class="icon fas fa-exclamation-circle"></i>Error!</h5>
|
||||
<ul>
|
||||
@if($productCount === 0 )
|
||||
<li> {{__('No products available!')}}</li>
|
||||
@endif
|
||||
|
||||
@if($nodeCount === 0 )
|
||||
<li>{{__('No nodes available!')}}</li>
|
||||
@endif
|
||||
|
||||
@if(count($nests) === 0 )
|
||||
<li>{{__('No nests available!')}}</li>
|
||||
@endif
|
||||
|
||||
@if(count($eggs) === 0 )
|
||||
<li>{{__('No eggs available!')}}</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
<div x-show="loading" class="overlay dark">
|
||||
<i class="fas fa-2x fa-sync-alt"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label for="name">* Name</label>
|
||||
<input id="name" name="name" type="text" required="required"
|
||||
class="form-control @error('name') is-invalid @enderror">
|
||||
<label for="name">{{__('Name')}}</label>
|
||||
<input x-model="name" id="name" name="name" type="text" required="required"
|
||||
class="form-control @error('name') is-invalid @enderror">
|
||||
|
||||
@error('name')
|
||||
<div class="invalid-feedback">
|
||||
Please fill out this field.
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
Please fill out this field.
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<input id="description" name="description" type="text"
|
||||
class="form-control @error('description') is-invalid @enderror">
|
||||
|
||||
@error('description')
|
||||
<div class="invalid-feedback">
|
||||
Please fill out this field.
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="nest">{{__('Software')}}</label>
|
||||
<select class="custom-select"
|
||||
name="nest"
|
||||
id="nest"
|
||||
x-model="selectedNest"
|
||||
@change="setNests(); $refs.egg.selectedIndex = '0'">
|
||||
<option selected disabled
|
||||
value="null">{{count($nests) > 0 ? __('Please select software..') : __('---')}}</option>
|
||||
@foreach ($nests as $nest)
|
||||
<option value="{{ $nest->id }}">{{ $nest->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location_id">* Server location</label>
|
||||
<div>
|
||||
|
||||
<select id="node_id" name="node_id" required="required"
|
||||
class="custom-select @error('node_id') is-invalid @enderror">
|
||||
<option selected disabled hidden value="">Please Select ...</option>
|
||||
@foreach ($locations as $location)
|
||||
<optgroup label="{{ $location->name }}">
|
||||
@foreach ($location->nodes as $node)
|
||||
@if (!$node->disabled)
|
||||
<option value="{{ $node->id }}">{{ $node->name }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('node_id')
|
||||
<div class="invalid-feedback">
|
||||
Please fill out this field.
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="egg">{{__('Configuration')}}</label>
|
||||
<div>
|
||||
<select id="egg"
|
||||
name="egg"
|
||||
x-ref="egg"
|
||||
:disabled="eggs.length == 0"
|
||||
x-model="selectedEgg"
|
||||
@change="fetchNodes(); $refs.node.selectedIndex = '0'"
|
||||
required="required"
|
||||
class="custom-select">
|
||||
<option x-text="getEggInputText()"
|
||||
selected disabled hidden value="null"></option>
|
||||
<template x-for="egg in eggs" :key="egg.id">
|
||||
<option x-text="egg.name" :value="egg.id"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="egg_id">* Server configuration</label>
|
||||
<div>
|
||||
<select id="egg_id" name="egg_id" required="required"
|
||||
class="custom-select @error('egg_id') is-invalid @enderror">
|
||||
<option selected disabled hidden value="">Please Select ...</option>
|
||||
@foreach ($nests as $nest)
|
||||
<optgroup label="{{ $nest->name }}">
|
||||
@foreach ($nest->eggs as $egg)
|
||||
<option value="{{ $egg->id }}">{{ $egg->name }}</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('egg_id')
|
||||
<div class="invalid-feedback">
|
||||
Please fill out this field.
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="product_id">* Resource Configuration</label>
|
||||
<div>
|
||||
<select id="product_id" name="product_id" required="required"
|
||||
class="custom-select @error('product_id') is-invalid @enderror">
|
||||
<option selected disabled hidden value="">Please Select...</option>
|
||||
@foreach ($products as $product)
|
||||
<option value="{{ $product->id }}" @if ($product->minimum_credits == -1 && Auth::user()->credits >= $minimum_credits)
|
||||
@elseif ($product->minimum_credits != -1 && Auth::user()->credits >=
|
||||
$product->minimum_credits)
|
||||
@else
|
||||
disabled
|
||||
@endif
|
||||
>{{ $product->name }}
|
||||
({{ $product->description }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<label for="node">{{__('Node')}}</label>
|
||||
<select name="node"
|
||||
id="node"
|
||||
x-ref="node"
|
||||
x-model="selectedNode"
|
||||
:disabled="!fetchedNodes"
|
||||
@change="fetchProducts();"
|
||||
class="custom-select">
|
||||
<option
|
||||
x-text="getNodeInputText()"
|
||||
disabled selected value="null"></option>
|
||||
<template x-for="node in nodes" :key="node.id">
|
||||
<option x-text="node.name" :value="node.id"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@error('product_id')
|
||||
<div class="invalid-feedback">
|
||||
Please fill out this field.
|
||||
|
||||
<div class="form-group">
|
||||
<label for="product">{{__('Resources')}}</label>
|
||||
<select name="product"
|
||||
id="product"
|
||||
x-ref="product"
|
||||
:disabled="!fetchedProducts"
|
||||
x-model="selectedProduct"
|
||||
class="custom-select">
|
||||
<option
|
||||
x-text="getProductInputText()"
|
||||
disabled selected value="null"></option>
|
||||
<template x-for="product in products" :key="product.id">
|
||||
<option x-text="product.name + ' (' + product.description + ')'"
|
||||
:value="product.id"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="text-muted">{{__('Server details')}}</span>
|
||||
</h4>
|
||||
<ul class="list-group mb-3">
|
||||
<li class="list-group-item d-flex justify-content-between lh-condensed">
|
||||
<div>
|
||||
<h6 class="my-0">{{__('Software')}}</h6>
|
||||
<small class="text-muted">Brief description</small>
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group text-right">
|
||||
<input type="submit" class="btn btn-primary mt-3" value="Submit"
|
||||
onclick="this.disabled=true;this.value='Creating, please wait...';this.form.submit();">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between lh-condensed">
|
||||
<div>
|
||||
<h6 class="my-0">{{__('Configuration')}}</h6>
|
||||
<small class="text-muted">Brief description</small>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="list-group-item d-flex justify-content-between lh-condensed">
|
||||
<div>
|
||||
<h6 class="my-0">{{__('Node')}}</h6>
|
||||
<small class="text-muted">Brief description</small>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="list-group-item d-flex justify-content-between lh-condensed">
|
||||
<div>
|
||||
<h6 class="my-0">{{__('Resources')}}</h6>
|
||||
<small class="text-muted">Brief description</small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul x-show="selectedProduct" class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span>{{CREDITS_DISPLAY_NAME}} {{__('per month')}}</span>
|
||||
<strong x-text="selectedProduct"></strong>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END CUSTOM CONTENT -->
|
||||
|
||||
<!-- END FORM -->
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<!-- END CONTENT -->
|
||||
|
||||
|
||||
<script>
|
||||
function serverApp() {
|
||||
return {
|
||||
loading: false,
|
||||
fetchedNodes: false,
|
||||
fetchedProducts: false,
|
||||
|
||||
name: null,
|
||||
selectedNest: null,
|
||||
selectedEgg: null,
|
||||
selectedNode: null,
|
||||
selectedProduct: null,
|
||||
|
||||
nests: {!! $nests !!},
|
||||
eggsSave:{!! $eggs !!}, //store back-end eggs
|
||||
eggs: [],
|
||||
nodes: [],
|
||||
products: [],
|
||||
|
||||
|
||||
/**
|
||||
* @description set available eggs based on the selected nest
|
||||
* @note called whenever a nest is selected
|
||||
* @see selectedNest
|
||||
*/
|
||||
setNests() {
|
||||
this.fetchedNodes = false;
|
||||
this.fetchedProducts = false;
|
||||
this.nodes = [];
|
||||
this.products = [];
|
||||
|
||||
this.eggs = this.eggsSave.filter(egg => egg.nest_id == this.selectedNest)
|
||||
},
|
||||
|
||||
/**
|
||||
* @description fetch all available locations based on the selected egg
|
||||
* @note called whenever a server configuration is selected
|
||||
* @see selectedEg
|
||||
*/
|
||||
async fetchNodes() {
|
||||
this.loading = true;
|
||||
this.fetchedNodes = false;
|
||||
this.fetchedProducts = false;
|
||||
this.nodes = [];
|
||||
this.products = [];
|
||||
|
||||
let response = await axios.get(`{{route('products.nodes.egg')}}/${this.selectedEgg}`)
|
||||
.catch(console.error)
|
||||
|
||||
this.fetchedNodes = true;
|
||||
this.nodes = response.data
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @description fetch all available products based on the selected node
|
||||
* @note called whenever a node is selected
|
||||
* @see selectedNode
|
||||
*/
|
||||
async fetchProducts() {
|
||||
this.loading = true;
|
||||
this.fetchedProducts = false;
|
||||
this.products = [];
|
||||
|
||||
let response = await axios.get(`{{route('products.products.node')}}/${this.selectedNode}`)
|
||||
.catch(console.error)
|
||||
|
||||
this.fetchedProducts = true;
|
||||
this.products = response.data
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
getNodeInputText() {
|
||||
if (this.fetchedNodes) {
|
||||
if (this.nodes.length > 0) {
|
||||
return '{{__('Please select a node...')}}';
|
||||
}
|
||||
return '{{__('No nodes found matching current configuration')}}'
|
||||
}
|
||||
return '{{__('---')}}';
|
||||
},
|
||||
|
||||
getProductInputText() {
|
||||
if (this.fetchedProducts) {
|
||||
if (this.products.length > 0) {
|
||||
return '{{__('Please select a resource...')}}';
|
||||
}
|
||||
return '{{__('No resources found matching current configuration')}}'
|
||||
}
|
||||
return '{{__('---')}}';
|
||||
},
|
||||
|
||||
getEggInputText() {
|
||||
if (this.selectedNest) {
|
||||
return '{{__('Please select a configuration...')}}';
|
||||
}
|
||||
return '{{__('---')}}';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
|
|
|
@ -16,6 +16,7 @@ use App\Http\Controllers\Admin\VoucherController;
|
|||
use App\Http\Controllers\Auth\SocialiteController;
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\NotificationController;
|
||||
use App\Http\Controllers\ProductController as FrontProductController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\ServerController;
|
||||
use App\Http\Controllers\StoreController;
|
||||
|
@ -54,6 +55,12 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
|
|||
Route::resource('profile', ProfileController::class);
|
||||
Route::resource('store', StoreController::class);
|
||||
|
||||
#server create utility routes (product)
|
||||
#routes made for server create page to fetch product info
|
||||
Route::get('/products/nodes/egg/{egg?}' , [FrontProductController::class , 'getNodesBasedOnEgg'])->name('products.nodes.egg');
|
||||
Route::get('/products/products/node/{node?}' , [FrontProductController::class , 'getProductsBasedOnNode'])->name('products.products.node');
|
||||
|
||||
|
||||
#payments
|
||||
Route::get('checkout/{paypalProduct}', [PaymentController::class, 'checkOut'])->name('checkout');
|
||||
Route::get('payment/success', [PaymentController::class, 'success'])->name('payment.success');
|
||||
|
|
Loading…
Reference in a new issue