Browse Source

new server create page w.i.p

AVMG20 3 years ago
parent
commit
16a7d174e9

+ 59 - 0
app/Http/Controllers/ProductController.php

@@ -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();
+    }
+}

+ 52 - 56
app/Http/Controllers/ServerController.php

@@ -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() . '"');
+        }
+    }
 }

+ 1 - 1
app/Models/Egg.php

@@ -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);
     }
-
 }

+ 1 - 1
app/Models/Location.php

@@ -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);
         }
     }
+
 }

+ 2 - 1
app/Models/Product.php

@@ -12,7 +12,8 @@ use Spatie\Activitylog\Traits\LogsActivity;
 
 class Product extends Model
 {
-    use HasFactory, LogsActivity;
+    use HasFactory;
+    use LogsActivity;
 
     public $incrementing = false;
 

File diff suppressed because it is too large
+ 13803 - 1
package-lock.json


File diff suppressed because it is too large
+ 0 - 0
public/css/app.css.map


File diff suppressed because it is too large
+ 0 - 0
public/js/app.js


File diff suppressed because it is too large
+ 0 - 0
public/js/app.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/js/alpine.js


+ 1 - 0
resources/js/app.js

@@ -3,3 +3,4 @@ require('./slim.kickstart.min')
 require('./bootstrap');
 
 
+

+ 4 - 4
resources/js/bootstrap.js

@@ -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

+ 42 - 47
resources/views/admin/products/create.blade.php

@@ -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

+ 2 - 0
resources/views/layouts/main.blade.php

@@ -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"/>
 

+ 262 - 101
resources/views/servers/create.blade.php

@@ -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') }}">
-                            @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">
+            <!-- FORM -->
+            <div class="row">
+                <div class="col-md-8">
+                    <div class="card">
+                        <div class="card-header">
+                            <div class="card-title">{{__('Server configuration')}}</div>
+                        </div>
 
-                                @error('name')
-                                    <div class="invalid-feedback">
-                                        Please fill out this field.
-                                    </div>
-                                @enderror
+                        @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
 
-                            </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">
+                                    @if($nodeCount === 0 )
+                                        <li>{{__('No nodes available!')}}</li>
+                                    @endif
 
-                                @error('description')
-                                    <div class="invalid-feedback">
-                                        Please fill out this field.
-                                    </div>
-                                @enderror
+                                    @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="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>
+                                <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('node_id')
-                                    <div class="invalid-feedback">
-                                        Please fill out this field.
-                                    </div>
+                                @error('name')
+                                <div class="invalid-feedback">
+                                    Please fill out this field.
+                                </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 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="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>
 
-                                @error('product_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>
-                            <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 class="form-group">
+                                <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>
-                        </form>
 
+
+                            <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>
+                                </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

+ 7 - 0
routes/web.php

@@ -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');

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