diff --git a/app/Enums/AddonInputType.php b/app/Enums/AddonInputType.php
new file mode 100644
index 0000000..db7f1d3
--- /dev/null
+++ b/app/Enums/AddonInputType.php
@@ -0,0 +1,28 @@
+ 'Số',
+ self::SLIDER => 'Thanh trượt',
+ self::SELECT => 'Chọn',
+ };
+ }
+
+ public static function getOptions(): array
+ {
+ return [
+ self::NUMBER->value => self::NUMBER->label(),
+ self::SLIDER->value => self::SLIDER->label(),
+ self::SELECT->value => self::SELECT->label(),
+ ];
+ }
+}
diff --git a/app/Enums/AddonType.php b/app/Enums/AddonType.php
new file mode 100644
index 0000000..51e2eae
--- /dev/null
+++ b/app/Enums/AddonType.php
@@ -0,0 +1,33 @@
+ 'VPS',
+ self::HOSTING => 'Hosting',
+ self::DOMAIN => 'Domain',
+ };
+ }
+
+ public static function getOptions(): array
+ {
+ return [
+ self::VPS->value => self::VPS->label(),
+ self::HOSTING->value => self::HOSTING->label(),
+ self::DOMAIN->value => self::DOMAIN->label(),
+ ];
+ }
+
+ public static function values(): array
+ {
+ return array_column(self::cases(), 'value');
+ }
+}
diff --git a/app/Http/Controllers/admin/AddonController.php b/app/Http/Controllers/admin/AddonController.php
index ec80bd4..2c69241 100644
--- a/app/Http/Controllers/admin/AddonController.php
+++ b/app/Http/Controllers/admin/AddonController.php
@@ -2,6 +2,7 @@
namespace App\Http\Controllers\admin;
+use App\Enums\AddonType;
use App\Http\Controllers\Controller;
use App\Http\Requests\AddOnRequest;
use App\Models\Addon;
@@ -102,22 +103,58 @@ class AddonController extends Controller
public function datatable(Request $request)
{
- $query = $this->addonRepository->filter(
- $request->all(),
- [],
- [],
- ['*'],
- true
- );
+ $addons = Addon::orderBy('sort_order')->get();
- return DataTables::eloquent($query)
- ->addColumn(
- 'action',
- function ($d) {
- return view('administrator.pages.addon.datatable.action', ['row' => $d])->render();
+ $grouped = $addons->groupBy(function ($item) {
+ return $item->type instanceof AddonType
+ ? $item->type->value
+ : $item->type;
+ });
+
+ $types = AddonType::values();
+ $data = [];
+
+ foreach ($types as $type) {
+ $typeEnum = AddonType::tryFrom($type);
+ $typeLabel = $typeEnum ? $typeEnum->label() : $type;
+ $typeAddons = $grouped->get($type, collect());
+
+ $childrenHtml = view('administrator.pages.addon.datatable.children', [
+ 'children' => $typeAddons
+ ])->render();
+
+ $data[] = [
+ 'id' => $type,
+ 'type' => $type,
+ 'name' => $typeLabel,
+ 'code' => $type,
+ 'children' => $typeAddons->toArray(),
+ 'children_html' => $childrenHtml,
+ 'status' => true,
+ ];
+ }
+
+ return DataTables::of(collect($data))
+ ->addColumn('details_control', function ($d) {
+ $hasChildren = !empty($d['children']) && count($d['children']) > 0;
+ if ($hasChildren) {
+ return '';
}
- )
- ->rawColumns(['status', 'action'])
+ return '';
+ })
+ ->addColumn('name', function ($d) {
+ return $d['name'];
+ })
+ ->addColumn('slug', function ($d) {
+ return $d['code'];
+ })
+ ->addColumn('is_active', function ($d) {
+ return 'Active';
+ })
+ ->addColumn('action', function ($d) {
+ return '';
+ })
+ ->rawColumns(['details_control', 'is_active', 'children_html'])
->make(true);
}
}
diff --git a/app/Models/Addon.php b/app/Models/Addon.php
index e735eb3..27189ee 100644
--- a/app/Models/Addon.php
+++ b/app/Models/Addon.php
@@ -2,6 +2,7 @@
namespace App\Models;
+use App\Enums\AddonType;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -12,7 +13,7 @@ class Addon extends Model
protected $table = 'product_addon';
protected $fillable = [
- 'type_id',
+ 'type',
'code',
'name',
'price',
@@ -20,7 +21,13 @@ class Addon extends Model
'max',
'step',
'prefix',
+ 'input_type',
'status',
'sort_order'
];
+
+ protected $casts = [
+ 'type' => AddonType::class,
+ 'status' => 'boolean',
+ ];
}
diff --git a/database/migrations/2026_02_13_104842_change_type_id_to_type_in_product_addon_table.php b/database/migrations/2026_02_13_104842_change_type_id_to_type_in_product_addon_table.php
new file mode 100644
index 0000000..2f4dd4b
--- /dev/null
+++ b/database/migrations/2026_02_13_104842_change_type_id_to_type_in_product_addon_table.php
@@ -0,0 +1,22 @@
+renameColumn('type_id', 'type');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('product_addon', function (Blueprint $table) {
+ $table->renameColumn('type', 'type_id');
+ });
+ }
+};
diff --git a/database/seeders/AddonSeeder.php b/database/seeders/AddonSeeder.php
index f482b31..79ec78a 100644
--- a/database/seeders/AddonSeeder.php
+++ b/database/seeders/AddonSeeder.php
@@ -10,42 +10,139 @@ class AddonSeeder extends Seeder
public function run(): void
{
$addons = [
+ // VPS Addons
[
- 'type_id' => 'vps',
+ 'type' => 'vps',
'code' => 'cpu',
'name' => 'CPU',
- 'price' => 100000,
- 'min' => 0,
- 'max' => 30,
+ 'price' => 20000,
+ 'min' => 1,
+ 'max' => 32,
'step' => 1,
'prefix' => 'vCore',
+ 'input_type' => 'number',
'status' => true,
'sort_order' => 0,
],
[
- 'type_id' => 'vps',
+ 'type' => 'vps',
'code' => 'ram',
'name' => 'RAM',
- 'price' => 50000,
- 'min' => 0,
- 'max' => 32,
+ 'price' => 100000,
+ 'min' => 1,
+ 'max' => 64,
'step' => 1,
'prefix' => 'GB',
+ 'input_type' => 'number',
'status' => true,
'sort_order' => 1,
],
[
- 'type_id' => 'vps',
+ 'type' => 'vps',
'code' => 'disk',
'name' => 'SSD',
'price' => 4000,
- 'min' => 0,
- 'max' => 1500,
- 'step' => 10,
+ 'min' => 25,
+ 'max' => 2000,
+ 'step' => 25,
'prefix' => 'GB',
+ 'input_type' => 'number',
'status' => true,
'sort_order' => 2,
],
+ [
+ 'type' => 'vps',
+ 'code' => 'gpu',
+ 'name' => 'GPU',
+ 'price' => 500000,
+ 'min' => 0,
+ 'max' => 4,
+ 'step' => 1,
+ 'prefix' => 'GB',
+ 'input_type' => 'select',
+ 'status' => true,
+ 'sort_order' => 3,
+ ],
+ [
+ 'type' => 'vps',
+ 'code' => 'bandwidth',
+ 'name' => 'Bandwidth',
+ 'price' => 50000,
+ 'min' => 100,
+ 'max' => 10000,
+ 'step' => 100,
+ 'prefix' => 'Mbps',
+ 'input_type' => 'slider',
+ 'status' => true,
+ 'sort_order' => 4,
+ ],
+
+ // Hosting Addons
+ [
+ 'type' => 'hosting',
+ 'code' => 'hosting_cpu',
+ 'name' => 'CPU',
+ 'price' => 15000,
+ 'min' => 1,
+ 'max' => 8,
+ 'step' => 1,
+ 'prefix' => 'Core',
+ 'input_type' => 'number',
+ 'status' => true,
+ 'sort_order' => 10,
+ ],
+ [
+ 'type' => 'hosting',
+ 'code' => 'hosting_ram',
+ 'name' => 'RAM',
+ 'price' => 25000,
+ 'min' => 512,
+ 'max' => 16384,
+ 'step' => 512,
+ 'prefix' => 'MB',
+ 'input_type' => 'number',
+ 'status' => true,
+ 'sort_order' => 11,
+ ],
+ [
+ 'type' => 'hosting',
+ 'code' => 'hosting_disk',
+ 'name' => 'Disk',
+ 'price' => 2000,
+ 'min' => 5,
+ 'max' => 500,
+ 'step' => 5,
+ 'prefix' => 'GB',
+ 'input_type' => 'number',
+ 'status' => true,
+ 'sort_order' => 12,
+ ],
+ [
+ 'type' => 'hosting',
+ 'code' => 'hosting_bandwidth',
+ 'name' => 'Bandwidth',
+ 'price' => 10000,
+ 'min' => 10,
+ 'max' => 1000,
+ 'step' => 10,
+ 'prefix' => 'GB',
+ 'input_type' => 'slider',
+ 'status' => true,
+ 'sort_order' => 13,
+ ],
+ [
+ 'type' => 'hosting',
+ 'code' => 'hosting_ip',
+ 'name' => 'IP Address',
+ 'price' => 30000,
+ 'min' => 0,
+ 'max' => 5,
+ 'step' => 1,
+ 'prefix' => 'IP',
+ 'input_type' => 'number',
+ 'status' => true,
+ 'sort_order' => 14,
+ ],
];
Addon::upsert($addons, ['code']);
diff --git a/database/seeders/OsSeeder.php b/database/seeders/OsSeeder.php
index 2a144c0..45e4919 100644
--- a/database/seeders/OsSeeder.php
+++ b/database/seeders/OsSeeder.php
@@ -17,7 +17,7 @@ class OsSeeder extends Seeder
[
'name' => 'Ubuntu',
'thumbnail' => 'ubuntu.png',
- 'module_host_id' => 1,
+ 'module_host_id' => null,
'status' => 1,
'versions' => [
'20.04 LTS',
@@ -28,7 +28,7 @@ class OsSeeder extends Seeder
[
'name' => 'CentOS',
'thumbnail' => 'centos.png',
- 'module_host_id' => 1,
+ 'module_host_id' => null,
'status' => 1,
'versions' => [
'7',
@@ -38,7 +38,7 @@ class OsSeeder extends Seeder
[
'name' => 'Debian',
'thumbnail' => 'debian.png',
- 'module_host_id' => 1,
+ 'module_host_id' => null,
'status' => 1,
'versions' => [
'10',
@@ -49,7 +49,7 @@ class OsSeeder extends Seeder
[
'name' => 'Windows Server',
'thumbnail' => 'windows.png',
- 'module_host_id' => 1,
+ 'module_host_id' => null,
'status' => 1,
'versions' => [
'2016',
diff --git a/resources/views/administrator/pages/addon/add.blade.php b/resources/views/administrator/pages/addon/add.blade.php
index 9253af3..be432b0 100644
--- a/resources/views/administrator/pages/addon/add.blade.php
+++ b/resources/views/administrator/pages/addon/add.blade.php
@@ -7,124 +7,171 @@
@section('breadcrumb')
@php
-$breadcrumbItems = [
- ['label' => 'Product'],
- ['label' => 'Product Addons'],
- ['label' => 'Product Addons List', 'url' => route('addon.index')],
- ['label' => 'Add new addon', 'active' => true],
-];
+ $breadcrumbItems = [
+ ['label' => 'Product'],
+ ['label' => 'Product Addons', 'url' => route('addon.index')],
+ ['label' => 'Add New Addon', 'active' => true],
+ ];
@endphp
| Name | +Prefix | +Min | +Max | +Step | +Price | +Status | +Action | +
|---|---|---|---|---|---|---|---|
| {{ $child['name'] }} | +{{ $child['prefix'] }} | +{{ $child['min'] }} | +{{ $child['max'] }} | +{{ $child['step'] }} | +{{ number_format($child['price']) }} VND | ++ @if($child['status']) + Active + @else + Inactive + @endif + | +
+
+
+
+
+
+
+ |
+
| No addons in this group | +|||||||