From b127fa7c669579c0e2410c457d56bb98557a1e6f Mon Sep 17 00:00:00 2001 From: pbao9 Date: Fri, 13 Feb 2026 10:07:24 +0700 Subject: [PATCH] feat: Implement addon functionality with schema updates, seeder, and administrator UI adjustments, including new favicons and a migration snapshot. --- all_migrations.md | 3841 +++++++++++++++++ app/Models/Addon.php | 4 +- .../migrations/2024_01_31_152449_addon.php | 2 + database/seeders/AddonSeeder.php | 6 + public/images/favi.svg | 9 + resources/images/favi.svg | 9 + .../administrator/layout/master.blade.php | 4 +- .../pages/module/vcenter/index.blade.php | 6 +- .../pages/product/categories/create.blade.php | 14 +- .../pages/product/categories/index.blade.php | 54 +- .../pages/product/create.blade.php | 12 +- .../views/components/button-c-r-m.blade.php | 98 +- .../views/components/data-table.blade.php | 907 ++-- 13 files changed, 4441 insertions(+), 525 deletions(-) create mode 100644 all_migrations.md create mode 100644 public/images/favi.svg create mode 100644 resources/images/favi.svg diff --git a/all_migrations.md b/all_migrations.md new file mode 100644 index 0000000..2059595 --- /dev/null +++ b/all_migrations.md @@ -0,0 +1,3841 @@ +## 0000_12_18_021711_create_administrative_regions_table.php +```php +integer('id')->primary(); + $table->string('name'); + $table->string('name_en'); + $table->string('code_name')->nullable(); + $table->string('code_name_en')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('administrative_regions'); + } +}; +``` + +## 0000_12_18_021712_create_administrative_units_table.php +```php +integer('id')->primary(); + $table->string('full_name')->nullable(); + $table->string('full_name_en')->nullable(); + $table->string('short_name')->nullable(); + $table->string('short_name_en')->nullable(); + $table->string('code_name')->nullable(); + $table->string('code_name_en')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('administrative_units'); + } +}; +``` + +## 0000_12_18_021714_create_provinces_table.php +```php +string('code', 20)->primary(); + $table->string('name'); + $table->string('name_en')->nullable(); + $table->string('full_name'); + $table->string('full_name_en')->nullable(); + $table->string('code_name')->nullable(); + $table->integer('administrative_unit_id')->nullable(); + + $table->foreign('administrative_unit_id') + ->references('id') + ->on('administrative_units') + ->onDelete('set null'); + + $table->index('administrative_unit_id', 'idx_provinces_unit'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('provinces'); + } +}; +``` + +## 0000_12_18_021716_create_wards_table.php +```php +string('code', 20)->primary(); + $table->string('name'); + $table->string('name_en')->nullable(); + $table->string('full_name')->nullable(); + $table->string('full_name_en')->nullable(); + $table->string('code_name')->nullable(); + $table->string('province_code', 20)->nullable(); + $table->integer('administrative_unit_id')->nullable(); + + $table->foreign('administrative_unit_id') + ->references('id') + ->on('administrative_units') + ->onDelete('set null'); + + $table->foreign('province_code') + ->references('code') + ->on('provinces') + ->onDelete('set null'); + + $table->index('province_code', 'idx_wards_province'); + $table->index('administrative_unit_id', 'idx_wards_unit'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('wards'); + } +}; +``` + +## 2014_10_12_000000_create_users_table.php +```php +id(); + $table->string('firstname', 255)->nullable(); + $table->string('lastname', 255)->nullable(); + $table->string('username', 100)->nullable(); + $table->string('email', 100)->nullable(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password')->nullable(); + $table->tinyInteger('status')->default(1); + $table->rememberToken(); + $table->timestamps(); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +}; +``` + +## 2014_10_12_100000_create_password_resets_table.php +```php +string('email')->index(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('password_resets'); + } +}; +``` + +## 2016_06_01_000001_create_oauth_auth_codes_table.php +```php +string('id', 100)->primary(); + $table->unsignedBigInteger('user_id')->index(); + $table->uuid('client_id'); + $table->text('scopes')->nullable(); + $table->boolean('revoked'); + $table->dateTime('expires_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('oauth_auth_codes'); + } +}; +``` + +## 2016_06_01_000002_create_oauth_access_tokens_table.php +```php +string('id', 100)->primary(); + $table->unsignedBigInteger('user_id')->nullable()->index(); + $table->uuid('client_id'); + $table->string('name')->nullable(); + $table->text('scopes')->nullable(); + $table->boolean('revoked'); + $table->timestamps(); + $table->dateTime('expires_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('oauth_access_tokens'); + } +}; +``` + +## 2016_06_01_000003_create_oauth_refresh_tokens_table.php +```php +string('id', 100)->primary(); + $table->string('access_token_id', 100)->index(); + $table->boolean('revoked'); + $table->dateTime('expires_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('oauth_refresh_tokens'); + } +}; +``` + +## 2016_06_01_000004_create_oauth_clients_table.php +```php +uuid('id')->primary(); + $table->unsignedBigInteger('user_id')->nullable()->index(); + $table->string('name'); + $table->string('secret', 100)->nullable(); + $table->string('provider')->nullable(); + $table->text('redirect'); + $table->boolean('personal_access_client'); + $table->boolean('password_client'); + $table->boolean('revoked'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('oauth_clients'); + } +}; +``` + +## 2016_06_01_000005_create_oauth_personal_access_clients_table.php +```php +bigIncrements('id'); + $table->uuid('client_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('oauth_personal_access_clients'); + } +}; +``` + +## 2019_08_19_000000_create_failed_jobs_table.php +```php +id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('failed_jobs'); + } +}; +``` + +## 2019_12_14_000001_create_personal_access_tokens_table.php +```php +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('personal_access_tokens'); + } +}; +``` + +## 2024_01_31_145201_customer.php +```php +id(); + $table->uuid('uuid')->unique(); + $table->string('fullname', 100)->nullable(); + $table->string('avatar', 100)->nullable(); + $table->integer('gender')->default(0); + $table->string('address', 100)->nullable(); + $table->string('state', 100)->nullable(); + $table->string('city', 100)->nullable(); + $table->integer('postcode')->nullable(); + $table->string('country', 100)->nullable(); + $table->string('company', 100)->nullable(); + $table->string('phone', 100)->unique()->nullable(); + $table->string('email', 100)->unique()->nullable(); + $table->string('email_verify', 100)->nullable(); + $table->string('email_hash', 100)->nullable(); + $table->tinyInteger('email_marketing')->default(1); + $table->tinyInteger('receive_deposit')->default(1); + $table->string('password')->nullable(); + $table->integer('credit')->default(0); + $table->integer('total')->default(0); + $table->string('identity_number')->nullable(); + $table->string('front_identity_number')->nullable(); + $table->string('back_identity_number')->nullable(); + $table->string('province_code', 20)->nullable(); + $table->string('ward_code', 20)->nullable(); + $table->string('remember_token', 100)->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + + $table->foreign('province_code') + ->references('code') + ->on('provinces') + ->onDelete('set null'); + + $table->foreign('ward_code') + ->references('code') + ->on('wards') + ->onDelete('set null'); + + $table->index('province_code', 'idx_users_province'); + $table->index('ward_code', 'idx_users_ward'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::dropIfExists('customer'); + } +}; +``` + +## 2024_01_31_145237_banned.php +```php +id(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('counter')->default(0); + $table->integer('due_date')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::table('banned',function (Blueprint $table) { + $table->dropForeign('customer_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_145738_user_log.php +```php +id()->autoIncrement(); + $table->string('agent')->nullable(); + $table->string('client_ip',100)->nullable(); + $table->string('username',100)->nullable(); + $table->text('description')->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::table('log',function (Blueprint $table){ + $table->dropForeign('customer_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_150016_notes.php +```php +id()->autoIncrement(); + $table->string('note')->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('user_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('note',function (Blueprint $table){ + $table->dropForeign('customer_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_150452_user__file.php +```php +id(); + $table->string('filename',100)->nullable(); + $table->string('file_url',100)->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::table('file',function (Blueprint $table){ + $table->dropForeign('customer_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_150810_type.php +```php +id(); + $table->string('name',255)->nullable(); + $table->string('icon',255)->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('group'); + } +}; +``` + +## 2024_01_31_150811_group.php +```php +id()->autoIncrement(); + $table->string('name', 255)->nullable(); + $table->integer('type_id')->default(0)->index(); + $table->string('slug')->nullable()->unique(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('group', function (Blueprint $table) { + $table->dropForeign('type_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_150900_create_product_categories_table.php +```php +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->foreignId('parent_id')->nullable() + ->constrained('product_categories') + ->nullOnDelete(); + + $table->boolean('is_active')->default(true); + $table->integer('sort_order')->default(0); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('product_categories'); + } +}; +``` + +## 2024_01_31_151000_create_products_table.php +```php +id(); + $table->uuid('uuid')->unique(); + + // Product type + $table->string('product_type', 50)->index(); + $table->unsignedBigInteger('product_categories_id')->index(); + + // Basic info + $table->string('name'); + $table->string('slug')->unique(); + $table->string('short_description')->nullable(); + $table->text('description')->nullable(); + $table->string('thumbnail')->nullable(); + $table->json('gallery')->nullable(); + + // Trial / stock + $table->integer('stock')->nullable(); + $table->boolean('is_unlimited')->default(true); + + // Hardware specs (VPS) + $table->string('cpu')->nullable(); + $table->string('ram')->nullable(); + $table->string('gpu')->nullable(); + $table->string('disk')->nullable(); + $table->string('bandwidth')->nullable(); + + // Extra + $table->json('features')->nullable(); + $table->json('metadata')->nullable(); + $table->decimal('setup_fee', 15, 2)->default(0); + $table->integer('trial_days')->default(0); + + + $table->boolean('is_active')->default(true); + $table->integer('sort_order')->default(0); + $table->timestamp('published_at')->nullable(); + + $table->foreign('product_categories_id') + ->references('id') + ->on('product_categories') + ->onDelete('cascade'); + + $table->softDeletes(); + $table->timestamps(); + }); + + + } + + public function down(): void + { + Schema::dropIfExists('products'); + } +}; +``` + +## 2024_01_31_151050_create_product_pricings_table.php +```php +id(); + + // FK + $table->foreignId('product_id') + ->constrained('products') + ->cascadeOnDelete(); + + // billing cycle type + $table->string('billing_cycle', 50); + /* + one_time + monthly + quarterly + semi_annually + annually + biennially (2 years) + triennially (3 years) + */ + + // pricing + $table->decimal('price', 15, 2); + $table->decimal('price_sale', 15, 2)->nullable(); + + + $table->string('currency', 10)->default('USD'); + + $table->boolean('is_active')->default(true); + + $table->timestamps(); + + // tránh trùng 1 chu kỳ trên 1 product + $table->unique(['product_id', 'billing_cycle']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('product_pricings'); + } +}; +``` + +## 2024_01_31_151053_template.php +```php +id()->autoIncrement(); + $table->integer('type_id'); + $table->string('name',100)->nullable(); + $table->string('subject',100)->nullable(); + $table->longText('message')->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('template'); + } +}; +``` + +## 2024_01_31_151827_billing.php +```php +id()->autoIncrement(); + $table->string('company',255)->nullable(); + $table->string('tax',100)->nullable(); + $table->string('address',100)->nullable(); + $table->string('email',100)->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('billing',function (Blueprint $table){ + $table->dropForeign('customer_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_152449_addon.php +```php +id(); + $table->string('type_id', 50)->default('vps')->index(); + $table->string('code', 50)->unique(); + $table->string('name', 100); + $table->string('prefix', 50)->nullable(); + $table->integer('min')->default(0); + $table->integer('max')->default(0); + $table->integer('step')->default(1); + $table->integer('price')->default(0); + $table->enum('input_type', ['number', 'slider', 'select']) + ->default('number'); + $table->boolean('status')->default(true); + $table->integer('sort_order')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('product_addon'); + } +}; +``` + +## 2024_01_31_152911_domain.php +```php +id()->autoIncrement(); + $table->string('name',100)->nullable(); + $table->integer('price')->nullable(); + $table->integer('price_sale')->nullable(); + $table->integer('renewal')->nullable(); + $table->integer('transfer')->nullable(); + $table->tinyInteger('auto_reg')->default(0); + $table->tinyInteger('type_id')->default(0)->index(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('domain',function (Blueprint $table){ + $table->dropForeign('group_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_153518_hosting.php +```php +id()->autoIncrement(); + $table->string('name', 255)->nullable(); + $table->string('slug')->nullable(); + $table->integer('price')->nullable(); + $table->integer('price_sale')->nullable(); + $table->integer('ram')->default(0)->nullable(); + $table->integer('disk')->default(125)->nullable(); + $table->integer('cpu')->default(1)->nullable(); + $table->integer('gpu')->default(0)->nullable(); + $table->integer('ip')->default(0)->nullable(); + $table->integer('bandwidth')->default(1000)->nullable(); + $table->integer('module_host_id')->nullable()->index(); + $table->text('description')->nullable(); + $table->string('template', 255)->nullable(); + $table->tinyInteger('type_id')->default(0)->index(); + $table->integer('group_id')->default(0)->index(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('hosting', function (Blueprint $table) { + $table->dropForeign('group_id'); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_154932_discount.php +```php +id()->autoIncrement(); + $table->string('code',100)->nullable(); + $table->integer('start')->nullable(); + $table->integer('end')->nullable(); + $table->integer('count')->default(0); + $table->integer('discount')->nullable(); + $table->integer('use')->default(0); + $table->integer('save')->default(0); + $table->tinyInteger('type')->default(1)->comment('1: %, 0: *.000.000'); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::dropIfExists('discount'); + } +}; +``` + +## 2024_01_31_155307_discount__customer.php +```php +bigInteger('discount_id')->default(0)->index(); + $table->bigInteger('customer_id')->default(0)->index(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('discount_customer',function (Blueprint $table) { + $table->dropForeign(['discount_id','customer_id']); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_155620_payment.php +```php +id()->autoIncrement(); + $table->string('name',100)->nullable(); + $table->tinyInteger('type')->default(0)->comment('0: thủ công,1: automation'); + $table->string('accountNo')->nullable(); + $table->string('bankName')->nullable(); + $table->string('bankCode')->nullable(); + $table->string('logo')->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('payment'); + } +}; +``` + +## 2024_01_31_155621_order.php +```php +id(); + $table->string('order_num', 255)->unique()->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('date')->nullable(); + $table->integer('user_id')->default(0); + $table->integer('discount_id')->default(0); + $table->integer('invoice_id')->default(0); + $table->integer('discount_value')->nullable(); + $table->integer('amount')->nullable(); + $table->text('note')->nullable(); + $table->string('client_ip', 100)->nullable(); + $table->tinyInteger('status')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::table('order', function (Blueprint $table) { + $table->dropForeign(['customer_id', 'discount_id']); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_155824_invoice.php +```php +id(); + $table->string('order_num', 255)->unique()->nullable(); + $table->integer('date')->nullable(); + $table->integer('due_date')->nullable(); + $table->integer('date_paid')->nullable(); + $table->integer('sub_total')->nullable(); + $table->integer('tax')->nullable(); + $table->integer('total')->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('order_id')->default(0)->index(); + $table->text('note')->nullable(); + $table->string('client_ip', 100)->nullable(); + $table->string('vm_id')->nullable()->index(); + $table->unsignedInteger('rebuild_count')->nullable()->default(0); + $table->tinyInteger('invoice_type')->default(1); + $table->tinyInteger('status')->default(0); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice', function (Blueprint $table) { + $table->dropForeign(['payment_id', 'customer_id']); + $table->drop(); + }); + } +}; +``` + +## 2024_01_31_163018_ticket.php +```php +id()->autoIncrement(); + $table->string('subject',255)->nullable(); + $table->integer('ticket_num')->nullable(); + $table->text('description')->nullable(); + $table->string('room',255)->nullable(); + $table->json('data')->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->tinyInteger('status')->default(1)->comment('0: close, 1: open'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ticket'); + } +}; +``` + +## 2024_01_31_163344_ticket_reply.php +```php +id()->autoIncrement(); + $table->bigInteger('ticket_id')->default(0)->index(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->text('message')->nullable(); + $table->unsignedBigInteger('user_id'); + $table->foreign('user_id')->references('id')->on('user')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ticket_reply'); + } +}; +``` + +## 2024_01_31_164507_domain_type.php +```php +id()->autoIncrement(); + $table->string('name',255)->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('domain_type'); + } +}; +``` + +## 2024_01_31_164730_module_domain.php +```php +id(); + $table->string('name', 255)->nullable(0); + $table->bigInteger('order_id')->default(0)->index(); + $table->integer('first_payment')->nullable(); + $table->integer('next_payment')->nullable(); + $table->unsignedBigInteger('module_dns_id'); + $table->integer('next_due_date')->nullable(); + $table->integer('next_invoice_date')->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('domain_type_id')->default(0)->index(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_domains'); + } +}; +``` + +## 2024_01_31_165505_module_dns_server.php +```php +id()->autoIncrement(); + $table->string('name',100)->default(0); + $table->string('user',100)->nullable(); + $table->text('password')->nullable(); + $table->integer('counter')->default(0); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_dns_server'); + } +}; +``` + +## 2024_01_31_165959_module_hosting_server.php +```php +id()->autoIncrement(); + $table->string('name',100)->nullable(); + $table->string('user',100)->nullable(); + $table->string('apikey',100)->nullable(); + $table->text('password')->nullable(); + $table->integer('counter')->default(0); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_hosting_server'); + } +}; +``` + +## 2024_02_01_080844_module_hosting.php +```php +id()->autoIncrement(); + $table->string('name',255)->nullable(); + $table->bigInteger('order_id')->default(0)->index(); + $table->integer('module_server_id')->default(0)->index(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('first_payment')->nullable(); + $table->integer('server_domain_id')->nullable(); + $table->integer('next_payment')->nullable(); + $table->integer('next_due_date')->nullable(); + $table->integer('next_invoice')->nullable(); + $table->string('username',255)->nullable(); + $table->text('password')->nullable(); + $table->tinyInteger('status')->default(1)->comment('1: Còn hạn;0: hết hạn'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_hostings'); + } +}; +``` + +## 2024_02_01_082942_v_center.php +```php +id()->autoIncrement(); + $table->string('host',100)->nullable(); + $table->string('user',100)->nullable(); + $table->text('password')->nullable(); + $table->string('token',255)->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_vcenter'); + } +}; +``` + +## 2024_02_01_090134_module_datacenter.php +```php +id()->autoIncrement(); + $table->string('name', 255)->nullable(); + $table->string('datacenter_id', 255)->nullable(); + $table->foreignId('vcenter_id')->constrained('module_vcenter')->cascadeOnDelete()->nullable()->index(); + $table->integer('status')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_datacenters'); + } +}; +``` + +## 2024_02_01_090537_module_host.php +```php +id(); + $table->string('name',100)->nullable(); + $table->string('host_id',255)->nullable(); + $table->string('template',255)->nullable(); + $table->foreignId('module_datacenter_id')->nullable()->constrained('module_datacenter', 'id', 'fk_mod_host_mod_dc'); + $table->string('console',255)->nullable(); + $table->string('iso_path',255)->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_host'); + } +}; +``` + +## 2024_02_01_091327_module_folder.php +```php +id()->autoIncrement(); + $table->string('name',255)->nullable(); + $table->string('folder',255)->nullable(); + $table->string('type',100)->nullable(); + $table->bigInteger('module_datacenter_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_folders'); + } +}; +``` + +## 2024_02_01_091707_module_datastore.php +```php +id(); + $table->string('name', 255)->nullable(); + $table->string('datastore', 255)->nullable(); + $table->string('type', 100)->nullable(); + $table->integer('space')->nullable(); + $table->foreignId('module_host_id')->index()->constrained('module_host')->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_datastore'); + } +}; +``` + +## 2024_02_01_092104_module_network.php +```php +id(); + $table->string('name', 255)->nullable(); + $table->string('type', 100)->nullable(); + $table->string('network', 100)->nullable(); + + $table->unsignedBigInteger('module_host_id')->nullable(); + + $table->foreign('module_host_id', 'fk_module_network_host') + ->references('id') + ->on('module_host') + ->nullOnDelete(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_network'); + } +}; +``` + +## 2024_02_01_094827_module_ipadress.php +```php +id()->autoIncrement(); + $table->string('vm_name',255)->nullable(); + $table->unsignedBigInteger('module_network_id')->nullable(); + $table->string('ip',100)->nullable(); + $table->string('gateway',100)->nullable(); + $table->string('netmask',100)->nullable(); + $table->string('dns',100)->nullable(); + $table->tinyInteger('status')->default(0)->comment('1: assign;0: free'); + $table->timestamps(); + $table->foreign('module_network_id', 'fk_module_ip_network') + ->references('id') + ->on('module_network') + ->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_ip_address'); + } +}; +``` + +## 2024_02_01_100912_module_snapshot_limit.php +```php +id()->autoIncrement(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->integer('limit')->default(1); + $table->integer('used')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_snapshot_limit'); + } +}; +``` + +## 2024_02_01_141640_setting.php +```php +id(); + $table->string('key')->nullable(); + $table->longText('value')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('setting'); + } +}; +``` + +## 2024_02_01_164056_create_permission_tables.php +```php +bigIncrements('id'); // permission id + $table->string('name'); // For MySQL 8.0 use string('name', 125); + $table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125); + $table->timestamps(); + + $table->unique(['name', 'guard_name']); + }); + + Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) { + $table->bigIncrements('id'); // role id + if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing + $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); + $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); + } + $table->string('name'); // For MySQL 8.0 use string('name', 125); + $table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125); + $table->timestamps(); + if ($teams || config('permission.testing')) { + $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); + } else { + $table->unique(['name', 'guard_name']); + } + }); + + Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { + $table->unsignedBigInteger($pivotPermission); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } else { + $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } + + }); + + Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { + $table->unsignedBigInteger($pivotRole); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } else { + $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } + }); + + Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { + $table->unsignedBigInteger($pivotPermission); + $table->unsignedBigInteger($pivotRole); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $tableNames = config('permission.table_names'); + + if (empty($tableNames)) { + throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + } + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +}; +``` + +## 2024_02_02_143120_recent_mail.php +```php +id()->autoIncrement(); + $table->bigInteger('template_id')->default(0)->index(); + $table->string('subject',255)->nullable(); + $table->string('to',255)->nullable(); + $table->string('cc',255)->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->text('message')->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mail'); + } +}; +``` + +## 2024_02_02_194120_os__group.php +```php +id()->autoIncrement(); + $table->string('name')->nullable(); + $table->string('thumbnail')->nullable(); + $table->unsignedBigInteger('module_host_id')->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + $table->foreign('module_host_id', 'fk_os_group_host') + ->references('id') + ->on('module_host') + ->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('os_group'); + } +}; +``` + +## 2024_02_02_395629_os.php +```php +id(); + $table->string('version', 100)->nullable(); + $table->unsignedBigInteger('os_group_id')->nullable(); + $table->string('template_id')->nullable(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + $table->foreign('os_group_id', 'fk_os_group_id')->references('id')->on('os_group')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('os'); + } +}; +``` + +## 2024_02_03_095630_module_vmware.php +```php +id()->autoIncrement(); + $table->string('name',255)->nullable(); + $table->bigInteger('module_ip_address_id')->default(0)->index(); + $table->integer('port')->nullable(); + $table->integer('order_id')->nullable(); + $table->integer('first_payment')->nullable(); + $table->integer('next_payment')->nullable(); + $table->integer('next_due_date')->nullable(); + $table->integer('next_invoice')->nullable(); + $table->string('user',100)->nullable(); + $table->text('password')->nullable(); + $table->string('hostname',100)->nullable(); + $table->string('vmware_id')->nullable(); + $table->bigInteger('os_id')->default(0)->index(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->tinyInteger('status')->default(1)->comment('1: Running;0:Off;2:Suspend'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_vmwares'); + } +}; +``` + +## 2024_02_04_100435_module_snapshot.php +```php +id()->autoIncrement(); + $table->string('name', 255)->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->bigInteger('module_vmware_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('module_snapshots'); + } +}; +``` + +## 2024_02_16_085257_create_jobs_table.php +```php +bigIncrements('id'); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('jobs'); + } +}; +``` + +## 2024_02_21_154350_v_p_s.php +```php +id()->autoIncrement(); + $table->string('name', 100)->nullable(); + $table->integer('cpu')->default(1); + $table->integer('ram')->default(1); + $table->integer('disk')->default(10); + $table->integer('gpu')->nullable(); + $table->text('description')->nullable(); + $table->integer('bandwidth')->default(100); + $table->integer('price')->nullable(); + $table->integer('price_sale')->nullable(); + $table->bigInteger('module_host_id')->default(0)->index(); + $table->integer('type_id')->default(0)->index(); + $table->integer('group_id')->default(0)->index(); + $table->tinyInteger('status')->default(1); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::dropIfExists('vps'); + } +}; +``` + +## 2024_02_26_124405_order___order_item.php +```php +id(); + $table->unsignedBigInteger('order_id')->index(); + $table->unsignedBigInteger('product_id')->nullable()->index(); + $table->string('billing_cycle', 50)->nullable(); + $table->integer('months')->default(0); + $table->unsignedBigInteger('os_id')->nullable()->index(); + $table->string('server_name')->nullable(); + $table->integer('quantity')->default(1); + $table->integer('price')->default(0); + $table->timestamps(); + + // Foreign Keys + $table->foreign('order_id')->references('id')->on('order')->onDelete('cascade'); + $table->foreign('product_id')->references('id')->on('products')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('order__order_item'); + } +}; +``` + +## 2024_03_18_202915_create_job_batches_table.php +```php +string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('job_batches'); + } +}; +``` + +## 2024_05_07_094540_post.php +```php +id()->autoIncrement(); + $table->string('name',255)->nullable(); + $table->string('slug',255)->unique()->nullable(); + $table->string('thumbnail',255)->nullable(); + $table->longText('content')->nullable(); + $table->tinyInteger('status')->default(1)->comment('0: Pending; 1: Published; 2: Draft'); + $table->timestamps(); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('posts'); + } +}; +``` + +## 2024_05_10_090436_staff_online.php +```php +id()->autoIncrement(); + $table->string('sessions_id')->nullable(); + $table->integer('user_id')->default(0)->index(); + $table->integer('last_visited')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('staff_online'); + } +}; +``` + +## 2024_05_13_165053_transaction.php +```php +id()->autoIncrement(); + $table->string('trans')->nullable(); + $table->integer('amount')->nullable(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + Schema::dropIfExists('transactions'); + } +}; +``` + +## 2024_05_15_082905_customer_log.php +```php +id()->autoIncrement(); + $table->bigInteger('customer_id')->default(0)->index(); + $table->string('action',255)->nullable(); + $table->string('client_ip',255)->nullable(); + $table->string('agent')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('customer_logs'); + } +}; +``` + +## 2025_01_31_160440_invoice_item.php +```php +id()->autoIncrement(); + $table->bigInteger('invoice_id')->default(0)->index(); + $table->unsignedBigInteger('product_id')->nullable()->index(); + $table->integer('months')->default(0); + $table->unsignedBigInteger('os_id')->nullable()->index(); + $table->string('server_name')->nullable(); + $table->integer('quantity')->default(1); + $table->tinyInteger('invoice_type')->default(1); + $table->integer('price')->default(0); + $table->string('product_type', 50)->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoice_item'); + } +}; +``` + +## 2025_04_26_101238_create_menus_table.php +```php +id(); + $table->string('title')->nullable(); + $table->string('icon')->nullable(); + $table->string('slug')->nullable(); + $table->string('route_name')->nullable(); + $table->tinyInteger('sort_by')->nullable(); + $table->tinyInteger('is_active')->nullable(); + $table->nestedSet(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('menus'); + } +}; +``` + +## 2025_05_16_215246_create_carts_table.php +```php +id(); + $table->unsignedBigInteger('product_id'); + $table->unsignedBigInteger('customer_id'); + $table->unsignedTinyInteger('type_product'); + $table->integer('months')->default(0); + $table->timestamps(); + + $table->index(['customer_id', 'type_product', 'product_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('carts'); + } +}; +``` + +## 2025_05_20_161102_create_order_addons_table.php +```php +id(); + $table->unsignedBigInteger('order_item_id')->index(); + $table->unsignedBigInteger('addon_id')->index(); + $table->unsignedInteger('qty')->default(1); + $table->unsignedInteger('price'); + $table->timestamps(); + + // Foreign Keys + $table->foreign('order_item_id')->references('id')->on('order__order_item')->onDelete('cascade'); + $table->foreign('addon_id')->references('id')->on('product_addon')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('order__order_addons'); + } +}; +``` + +## 2025_05_21_154940_create_invoice_addons_table.php +```php +id(); + $table->unsignedBigInteger('invoice_item_id')->index(); // FK đến order_items + $table->unsignedBigInteger('addon_id'); + $table->unsignedInteger('qty')->default(1); + $table->unsignedInteger('price'); // đơn giá tại thời điểm mua + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('invoice_addons'); + } +}; +``` + +## 2025_07_14_162409_create_product_vps_os_table.php +```php +id(); + $table->unsignedBigInteger('product_id')->index(); + $table->integer('product_type')->nullable(); + $table->unsignedBigInteger('os_id')->index(); + + $table->foreign('product_id')->references('id')->on('vps')->onDelete('cascade'); + $table->foreign('os_id')->references('id')->on('os')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('product_os'); + } +}; +``` + +## 2025_07_15_084129_add_customer_id_index_to_carts_table.php +```php +index('customer_id', 'carts_customer_id_index'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('carts', function (Blueprint $table) { + $table->dropIndex('carts_customer_id_index'); + }); + } +}; +``` + +## 2025_07_15_151140_create_edit_column_invoice_table.php +```php +date('date_tmp')->nullable()->after('date'); + $table->date('due_date_tmp')->nullable()->after('due_date'); + $table->date('date_paid_tmp')->nullable()->after('date_paid'); + }); + + // 2. Chuyển giá trị từ INT → DATE + DB::statement("UPDATE invoice SET date_tmp = FROM_UNIXTIME(date) WHERE date IS NOT NULL"); + DB::statement("UPDATE invoice SET due_date_tmp = FROM_UNIXTIME(due_date) WHERE due_date IS NOT NULL"); + DB::statement("UPDATE invoice SET date_paid_tmp = FROM_UNIXTIME(date_paid) WHERE date_paid IS NOT NULL"); + + // 3. Xoá cột cũ + Schema::table('invoice', function (Blueprint $table) { + $table->dropColumn(['date', 'due_date', 'date_paid']); + }); + + // 4. Đổi tên cột tạm thành tên cũ + Schema::table('invoice', function (Blueprint $table) { + $table->renameColumn('date_tmp', 'date'); + $table->renameColumn('due_date_tmp', 'due_date'); + $table->renameColumn('date_paid_tmp', 'date_paid'); + }); + } + + public function down(): void + { + // 1. Thêm 3 cột tạm kiểu INT + Schema::table('invoice', function (Blueprint $table) { + $table->integer('date_int')->nullable(); + $table->integer('due_date_int')->nullable(); + $table->integer('date_paid_int')->nullable(); + }); + + // 2. Convert DATE → Unix timestamp + DB::statement("UPDATE invoice SET date_int = UNIX_TIMESTAMP(date) WHERE date IS NOT NULL"); + DB::statement("UPDATE invoice SET due_date_int = UNIX_TIMESTAMP(due_date) WHERE due_date IS NOT NULL"); + DB::statement("UPDATE invoice SET date_paid_int = UNIX_TIMESTAMP(date_paid) WHERE date_paid IS NOT NULL"); + + // 3. Xoá cột kiểu DATE + Schema::table('invoice', function (Blueprint $table) { + $table->dropColumn(['date', 'due_date', 'date_paid']); + }); + + // 4. Đổi tên lại + Schema::table('invoice', function (Blueprint $table) { + $table->renameColumn('date_int', 'date'); + $table->renameColumn('due_date_int', 'due_date'); + $table->renameColumn('date_paid_int', 'date_paid'); + }); + } +}; +``` + +## 2025_07_30_160731_create_ip_pools_table.php +```php +id(); + $table->string('ip_address'); + $table->unsignedBigInteger('module_network_id'); + $table->foreign('module_network_id')->references('id')->on('module_network'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ip_pools'); + } +}; +``` + +## 2025_08_03_230748_create_reinstall_logs_table.php +```php +id(); + + // Liên kết đến bảng sản phẩm (VPS/Hosting) + $table->unsignedBigInteger('product_id'); + $table->unsignedTinyInteger('product_type'); // 3 = Hosting, 4 = VPS + + // Liên kết tới user khởi tạo + $table->unsignedBigInteger('custome_id')->nullable(); + + // Template & OS được sử dụng + $table->unsignedBigInteger('os_id')->nullable(); // ID bảng operating_systems + $table->string('template_id')->nullable(); // VMware Template ID + + // IP hiện tại (nếu cần) + $table->string('ip_address')->nullable(); + + // Trạng thái và kết quả + $table->enum('status', ['pending', 'success', 'failed'])->default('pending'); + $table->text('error_message')->nullable(); + + // Metadata khác + $table->string('initiator')->nullable(); // 'user', 'admin', 'system' + $table->text('note')->nullable(); // Ghi chú tự do + + $table->timestamps(); + + // Index + $table->index(['product_id', 'product_type']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('reinstall_logs'); + } +}; +``` + +## 2026_01_17_143127_update_column_snapshot.php +```php +text('description')->nullable()->after('name'); + + // Async snapshot status + $table->enum('status', [ + 'pending', + 'processing', + 'success', + 'failed' + ])->default('pending')->index()->after('module_vmware_id'); + + // vCenter mapping + $table->string('snapshot_ref')->nullable()->comment('Snapshot ID / moRef from vCenter'); + $table->string('vcenter_task_id')->nullable()->comment('Task ID from vCenter'); + + // Error handling + $table->text('error_message')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_snapshot', function (Blueprint $table) { + $table->dropColumn([ + 'description', + 'status', + 'snapshot_ref', + 'vcenter_task_id', + 'error_message', + ]); + }); + } +}; +``` + +## 2026_01_17_151930_update_column_module_snapshot.php +```php +unsignedBigInteger('size_bytes') + ->nullable() + ->comment('Snapshot size in bytes'); + + $table->decimal('size_gb', 10, 2) + ->nullable() + ->comment('Snapshot size in GB'); + + $table->timestamp('size_last_synced_at') + ->nullable() + ->comment('Last time snapshot size was synced'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_snapshot', function (Blueprint $table) { + // + }); + } +}; +``` + +## 2026_01_27_151851_update_forigin_key_module_ip_table.php +```php +unsignedBigInteger('module_network_id')->nullable()->default(null)->change(); + $table->foreign('module_network_id')->references('id')->on('module_network')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_ip_address', function (Blueprint $table) { + $table->dropForeign(['module_network_id']); + $table->bigInteger('module_network_id')->default(0)->nullable(false)->change(); + }); + } +}; +``` + +## 2026_01_27_172000_add_foreign_keys_to_order_table.php +```php +unsignedBigInteger('customer_id')->nullable()->default(null)->change(); + $table->unsignedBigInteger('user_id')->nullable()->default(null)->change(); + $table->unsignedBigInteger('discount_id')->nullable()->default(null)->change(); + $table->unsignedBigInteger('invoice_id')->nullable()->default(null)->change(); + }); + + // 2. Convert default 0 values to null to ensure foreign key integrity + DB::table('order')->where('customer_id', 0)->update(['customer_id' => null]); + DB::table('order')->where('user_id', 0)->update(['user_id' => null]); + DB::table('order')->where('discount_id', 0)->update(['discount_id' => null]); + DB::table('order')->where('invoice_id', 0)->update(['invoice_id' => null]); + + // 3. Add foreign key constraints + Schema::table('order', function (Blueprint $table) { + $table->foreign('customer_id')->references('id')->on('customers')->onDelete('set null'); + $table->foreign('user_id')->references('id')->on('user')->onDelete('set null'); + $table->foreign('discount_id')->references('id')->on('discount')->onDelete('set null'); + $table->foreign('invoice_id')->references('id')->on('invoice')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('order', function (Blueprint $table) { + // Drop foreign keys + $table->dropForeign(['customer_id']); + $table->dropForeign(['user_id']); + $table->dropForeign(['discount_id']); + $table->dropForeign(['invoice_id']); + + // Revert columns to previous state (integers/bigInteger with default 0) + // Note: This matches the original migration definitions + $table->bigInteger('customer_id')->default(0)->nullable(false)->change(); + $table->integer('user_id')->default(0)->nullable(false)->change(); + $table->integer('discount_id')->default(0)->nullable(false)->change(); + $table->integer('invoice_id')->default(0)->nullable(false)->change(); + }); + + // Restore 0 values where null (optional, but keeps consistency with down()) + DB::table('order')->whereNull('customer_id')->update(['customer_id' => 0]); + DB::table('order')->whereNull('user_id')->update(['user_id' => 0]); + DB::table('order')->whereNull('discount_id')->update(['discount_id' => 0]); + DB::table('order')->whereNull('invoice_id')->update(['invoice_id' => 0]); + } +}; +``` + +## 2026_01_29_120313_create_virtual_machines_table.php +```php +id(); + $table->uuid('uuid')->unique(); + + $table->string('vm_id')->unique(); + $table->string('name'); + $table->string('power_state')->nullable(); + $table->string('folder')->nullable(); + $table->boolean('is_template')->default(false); + // Specs snapshot + $table->integer('cpu')->nullable(); + $table->integer('ram_mb')->nullable(); + // Mapping + $table->boolean('is_system')->default(false); // vCLS, system VM + $table->timestamp('last_synced_at')->nullable(); + $table->foreignId('datacenter_id')->constrained('module_datacenter')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('virtual_machines'); + } +}; +``` + +## 2026_01_30_170136_update_module_datacenter_id_in_module_host_table.php +```php +unsignedBigInteger('module_datacenter_id')->nullable()->change(); + + $table->foreign('module_datacenter_id') + ->references('id') + ->on('module_datacenter') + ->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_host', function (Blueprint $table) { + // Drop the foreign key first + $table->dropForeign(['module_datacenter_id']); + + // Revert column type (assuming it was bigInteger with default 0 based on previous context) + $table->bigInteger('module_datacenter_id')->default(0)->change(); + }); + } +}; +``` + +## 2026_01_30_170804_update_module_vcenter_id_in_module_datacenter.php +```php +unsignedBigInteger('vcenter_id')->nullable()->change(); + + $table->foreign('vcenter_id') + ->references('id') + ->on('module_vcenter') + ->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_datacenter', function (Blueprint $table) { + // + }); + } +}; +``` + +## 2026_01_31_093643_update_fk_column_module_folder.php +```php +unsignedBigInteger('module_datacenter_id')->nullable()->change(); + }); + + // Clean up orphan records that point to non-existent datacenters + DB::table('module_folder') + ->whereNotNull('module_datacenter_id') + ->whereNotExists(function ($query) { + $query->select(DB::raw(1)) + ->from('module_datacenter') + ->whereColumn('module_folder.module_datacenter_id', 'module_datacenter.id'); + }) + ->update(['module_datacenter_id' => null]); + + Schema::table('module_folder', function (Blueprint $table) { + $table->foreign('module_datacenter_id') + ->references('id') + ->on('module_datacenter') + ->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_folder', function (Blueprint $table) { + $table->dropForeign(['module_datacenter_id']); + // Revert changes if necessary, though restoring invalid data isn't possible. + // We just restore the foreign key constraint if that was the intent. + $table->foreign('module_datacenter_id')->references('id')->on('module_datacenter')->onDelete('cascade'); + }); + } +}; +``` + +## 2026_01_31_101947_update_table_module_network.php +```php +unsignedBigInteger('module_host_id')->nullable()->change(); + }); + + // Clean up orphan records that point to non-existent datacenters + DB::table('module_network') + ->whereNotNull('module_host_id') + ->whereNotExists(function ($query) { + $query->select(DB::raw(1)) + ->from('module_host') + ->whereColumn('module_network.module_host_id', 'module_host.id'); + }) + ->update(['module_host_id' => null]); + + Schema::table('module_network', function (Blueprint $table) { + $table->foreign('module_host_id') + ->references('id') + ->on('module_host') + ->cascadeOnDelete(); + }); + } + + public function down(): void + { + Schema::table('module_network', function (Blueprint $table) { + $table->unsignedBigInteger('module_host_id')->change(); + }); + } +}; +``` + +## 2026_02_03_114500_create_os_product_table.php +```php +id(); + $table->foreignId('product_id')->constrained('products')->onDelete('cascade'); + $table->foreignId('os_id')->constrained('os')->onDelete('cascade'); + $table->timestamps(); + + $table->unique(['product_id', 'os_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('os_product'); + } +}; +``` + +## 2026_02_06_100001_create_control_panels_table.php +```php +id(); + $table->string('name'); + $table->string('slug')->unique(); + $table->string('logo')->nullable(); + $table->text('description')->nullable(); + $table->boolean('is_active')->default(true); + $table->integer('sort_order')->default(0); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('control_panels'); + } +}; +``` + +## 2026_02_06_100002_create_product_control_panel_table.php +```php +id(); + $table->unsignedBigInteger('product_id'); + $table->unsignedBigInteger('control_panel_id'); + $table->decimal('price_modifier', 15, 2)->default(0)->comment('Phụ phí cho control panel này'); + $table->boolean('is_default')->default(false)->comment('Panel mặc định cho sản phẩm'); + $table->timestamps(); + + $table->foreign('product_id') + ->references('id') + ->on('products') + ->onDelete('cascade'); + + $table->foreign('control_panel_id') + ->references('id') + ->on('control_panels') + ->onDelete('cascade'); + + $table->unique(['product_id', 'control_panel_id']); + }); + } + + public function down(): void + { + Schema::dropIfExists('product_control_panel'); + } +}; +``` + +## 2026_02_08_210451_add_product_support_to_carts_and_order_items_tables.php +```php +string('billing_cycle', 50)->nullable()->after('months')->comment('Billing cycle: monthly, semi_annually, annually, etc.'); + } + if (!Schema::hasColumn('carts', 'product_pricing_id')) { + $table->unsignedBigInteger('product_pricing_id')->nullable()->after('billing_cycle')->comment('Link to product_pricings table'); + $table->index('product_pricing_id'); + } + + // Make type_product nullable for new Product system + $table->integer('type_product')->nullable()->change(); + }); + + // Add support for new Product system to order__order_item table + Schema::table('order__order_item', function (Blueprint $table) { + if (!Schema::hasColumn('order__order_item', 'billing_cycle')) { + $table->string('billing_cycle', 50)->nullable()->after('months')->comment('Billing cycle: monthly, semi_annually, annually, etc.'); + } + if (!Schema::hasColumn('order__order_item', 'product_pricing_id')) { + $table->unsignedBigInteger('product_pricing_id')->nullable()->after('billing_cycle')->comment('Link to product_pricings table'); + $table->index('product_pricing_id'); + } + + // Make product_type nullable for new Product system + if (Schema::hasColumn('order__order_item', 'product_type')) { + $table->string('product_type', 50)->nullable()->change(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('carts', function (Blueprint $table) { + $table->dropColumn(['billing_cycle', 'product_pricing_id']); + // $table->dropIndex(['product_pricing_id']); + }); + + Schema::table('order__order_item', function (Blueprint $table) { + $table->dropColumn(['billing_cycle', 'product_pricing_id']); + // $table->dropIndex(['product_pricing_id']); + }); + } +}; +``` + +## 2026_02_12_164158_create_infra_management_table.php +```php +id(); + $table->string('name')->nullable(); + $table->string('host')->unique()->comment('IP or Hostname'); + $table->string('username'); + $table->longText('password'); // Should be encrypted + $table->text('token')->nullable(); + $table->string('type')->default('vcenter')->comment('vcenter, esxi, etc.'); + $table->boolean('status')->default(true); + $table->text('description')->nullable(); + $table->json('meta_data')->nullable(); // For extra config + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('infra_management'); + } +}; +``` + diff --git a/app/Models/Addon.php b/app/Models/Addon.php index fd5f08f..e735eb3 100644 --- a/app/Models/Addon.php +++ b/app/Models/Addon.php @@ -12,6 +12,7 @@ class Addon extends Model protected $table = 'product_addon'; protected $fillable = [ + 'type_id', 'code', 'name', 'price', @@ -19,6 +20,7 @@ class Addon extends Model 'max', 'step', 'prefix', - 'status' + 'status', + 'sort_order' ]; } diff --git a/database/migrations/2024_01_31_152449_addon.php b/database/migrations/2024_01_31_152449_addon.php index 8b99cf7..b6ed9fe 100644 --- a/database/migrations/2024_01_31_152449_addon.php +++ b/database/migrations/2024_01_31_152449_addon.php @@ -14,6 +14,7 @@ return new class extends Migration { { Schema::create('product_addon', function (Blueprint $table) { $table->id(); + $table->string('type_id', 50)->default('vps')->index(); $table->string('code', 50)->unique(); $table->string('name', 100); $table->string('prefix', 50)->nullable(); @@ -24,6 +25,7 @@ return new class extends Migration { $table->enum('input_type', ['number', 'slider', 'select']) ->default('number'); $table->boolean('status')->default(true); + $table->integer('sort_order')->default(0)->index(); $table->timestamps(); }); } diff --git a/database/seeders/AddonSeeder.php b/database/seeders/AddonSeeder.php index b12f368..f482b31 100644 --- a/database/seeders/AddonSeeder.php +++ b/database/seeders/AddonSeeder.php @@ -11,6 +11,7 @@ class AddonSeeder extends Seeder { $addons = [ [ + 'type_id' => 'vps', 'code' => 'cpu', 'name' => 'CPU', 'price' => 100000, @@ -19,8 +20,10 @@ class AddonSeeder extends Seeder 'step' => 1, 'prefix' => 'vCore', 'status' => true, + 'sort_order' => 0, ], [ + 'type_id' => 'vps', 'code' => 'ram', 'name' => 'RAM', 'price' => 50000, @@ -29,8 +32,10 @@ class AddonSeeder extends Seeder 'step' => 1, 'prefix' => 'GB', 'status' => true, + 'sort_order' => 1, ], [ + 'type_id' => 'vps', 'code' => 'disk', 'name' => 'SSD', 'price' => 4000, @@ -39,6 +44,7 @@ class AddonSeeder extends Seeder 'step' => 10, 'prefix' => 'GB', 'status' => true, + 'sort_order' => 2, ], ]; diff --git a/public/images/favi.svg b/public/images/favi.svg new file mode 100644 index 0000000..a82baee --- /dev/null +++ b/public/images/favi.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/favi.svg b/resources/images/favi.svg new file mode 100644 index 0000000..a82baee --- /dev/null +++ b/resources/images/favi.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/views/administrator/layout/master.blade.php b/resources/views/administrator/layout/master.blade.php index cbe22b3..520e5ec 100644 --- a/resources/views/administrator/layout/master.blade.php +++ b/resources/views/administrator/layout/master.blade.php @@ -30,7 +30,7 @@ - + @foreach (config('assets.static.css') as $css) @@ -51,7 +51,7 @@
@include('administrator.layout.partials.header')
-
+
@yield('breadcrumb') @yield('tab') diff --git a/resources/views/administrator/pages/module/vcenter/index.blade.php b/resources/views/administrator/pages/module/vcenter/index.blade.php index 530d19b..b906521 100644 --- a/resources/views/administrator/pages/module/vcenter/index.blade.php +++ b/resources/views/administrator/pages/module/vcenter/index.blade.php @@ -43,16 +43,16 @@ @endsection @section('content') @if(auth()->guard('admins')->user()->can("manager module vcenter")) -
+
@include("administrator.pages.block.ModuleVCenter")
-
+
diff --git a/resources/views/administrator/pages/product/categories/create.blade.php b/resources/views/administrator/pages/product/categories/create.blade.php index 4893bfb..cf6e79f 100644 --- a/resources/views/administrator/pages/product/categories/create.blade.php +++ b/resources/views/administrator/pages/product/categories/create.blade.php @@ -39,7 +39,7 @@ @csrf
-
+
Basic Information
@@ -63,7 +63,8 @@ @if(isset($categories)) @foreach($categories as $category) + {{ $category->name }} + @endforeach @endif @@ -73,7 +74,7 @@
-
+
Classification
@@ -100,9 +101,10 @@
- Cancel - Create Category + Back + Create + Category @endsection diff --git a/resources/views/administrator/pages/product/categories/index.blade.php b/resources/views/administrator/pages/product/categories/index.blade.php index d66c2f6..9555baf 100644 --- a/resources/views/administrator/pages/product/categories/index.blade.php +++ b/resources/views/administrator/pages/product/categories/index.blade.php @@ -7,46 +7,46 @@ @section('breadcrumb') @php -$breadcrumbItems = [ - ['label' => 'Product'], - ['label' => 'Product Group'], - ['label' => 'Product Group List', 'active' => true] -]; + $breadcrumbItems = [ + ['label' => 'Product'], + ['label' => 'Product Group'], + ['label' => 'Product Group List', 'active' => true] + ]; @endphp @endsection @section('tab') @php -$tabs = [ - [ - 'label' => 'Product', - 'route' => 'products.index', - 'icon' => 'server', - ], - [ - 'label' => 'Product Group', - 'route' => 'products.group.index', - 'icon' => 'server', - ], -]; + $tabs = [ + [ + 'label' => 'Product', + 'route' => 'products.index', + 'icon' => 'server', + ], + [ + 'label' => 'Product Group', + 'route' => 'products.group.index', + 'icon' => 'server', + ], + ]; @endphp @endsection @section('content') @if (auth()->guard('admins')->user()->can('manager product')) -
-
+
+
@include('administrator.pages.block.product.btn')
-
+
+ ['key' => 'details_control', 'label' => '', 'orderable' => false, 'searchable' => false, 'className' => 'details-control text-center cursor-pointer'], + ['key' => 'name', 'label' => 'Group'], + ['key' => 'slug', 'label' => 'Slug'], + ['key' => 'is_active', 'label' => 'Status'], + ['key' => 'action', 'label' => 'Action', 'searchable' => false], + ]" />
@@ -164,4 +164,4 @@ $tabs = [ @endpush {{-- Render all modals outside datatable wrapper --}} -@stack('modals') +@stack('modals') \ No newline at end of file diff --git a/resources/views/administrator/pages/product/create.blade.php b/resources/views/administrator/pages/product/create.blade.php index 2c2041c..b16bc51 100644 --- a/resources/views/administrator/pages/product/create.blade.php +++ b/resources/views/administrator/pages/product/create.blade.php @@ -38,8 +38,8 @@
{{-- Left Column: Basic Info & Pricing --}}
-
-
+
+
Basic Information
@@ -195,8 +195,8 @@ {{-- Right Column: Classification & Settings --}}
-
-
+
+
Classification
@@ -305,8 +305,8 @@
-
-
+
+
Inventory & Limits
diff --git a/resources/views/components/button-c-r-m.blade.php b/resources/views/components/button-c-r-m.blade.php index ce92d60..aa05cbd 100644 --- a/resources/views/components/button-c-r-m.blade.php +++ b/resources/views/components/button-c-r-m.blade.php @@ -1,58 +1,58 @@ @php - $tag = 'button'; - $href = null; - $buttonType = $type; - - if ($type === 'delete' || $confirm) { $tag = 'button'; - $buttonType = 'button'; - $attributes = $attributes->merge([ - 'onclick' => "confirmDelete('$confirmUrl', '$confirmName')", - ]); - } elseif ($route) { - $href = route($route, $params); - $tag = 'a'; - } elseif ($url) { - $href = $url; - $tag = 'a'; - } elseif ($attributes->has('href')) { - $href = $attributes->get('href'); - $tag = 'a'; - } + $href = null; + $buttonType = $type; - $variants = [ - 'primary' => 'btn-primary', - 'secondary' => 'btn-secondary', - 'outline' => 'btn-outline-primary', - 'outline-primary' => 'btn-outline-primary', - 'outline-secondary' => 'btn-outline-secondary', - 'outline-success' => 'btn-outline-success', - 'outline-danger' => 'btn-outline-danger', - 'outline-warning' => 'btn-outline-warning', - 'outline-info' => 'btn-outline-info', - 'ghost' => 'btn-light', - 'link' => 'btn-link', - 'danger' => 'btn-danger', - 'destructive' => 'btn-danger', - 'success' => 'btn-success', - 'warning' => 'btn-warning', - 'info' => 'btn-info', - 'light' => 'btn-light', - 'dark' => 'btn-dark', - ]; + if ($type === 'delete' || $confirm) { + $tag = 'button'; + $buttonType = 'button'; + $attributes = $attributes->merge([ + 'onclick' => "confirmDelete('$confirmUrl', '$confirmName')", + ]); + } elseif ($route) { + $href = route($route, $params); + $tag = 'a'; + } elseif ($url) { + $href = $url; + $tag = 'a'; + } elseif ($attributes->has('href')) { + $href = $attributes->get('href'); + $tag = 'a'; + } - $sizes = [ - 'default' => '', - 'sm' => 'btn-sm', - 'lg' => 'btn-lg', - 'xs' => 'btn-xs', - 'icon' => 'btn-icon', - ]; + $variants = [ + 'primary' => 'btn-primary', + 'secondary' => 'btn-secondary', + 'outline' => 'btn-outline-primary', + 'outline-primary' => 'btn-outline-primary', + 'outline-secondary' => 'btn-outline-secondary', + 'outline-success' => 'btn-outline-success', + 'outline-danger' => 'btn-outline-danger', + 'outline-warning' => 'btn-outline-warning', + 'outline-info' => 'btn-outline-info', + 'ghost' => 'btn-light', + 'link' => 'btn-link', + 'danger' => 'btn-danger', + 'destructive' => 'btn-danger', + 'success' => 'btn-success', + 'warning' => 'btn-warning', + 'info' => 'btn-info', + 'light' => 'btn-light', + 'dark' => 'btn-dark', + ]; - $variantClass = $variants[$variant ?? 'primary'] ?? $variants['primary']; - $sizeClass = $sizes[$size ?? 'default'] ?? ''; + $sizes = [ + 'default' => '', + 'sm' => 'btn-sm', + 'lg' => 'btn-lg', + 'xs' => 'btn-xs', + 'icon' => 'btn-icon', + ]; - $classes = "btn d-flex align-items-center {$variantClass} {$sizeClass}"; + $variantClass = $variants[$variant ?? 'primary'] ?? $variants['primary']; + $sizeClass = $sizes[$size ?? 'default'] ?? ''; + + $classes = "btn d-flex align-items-center rounded-4 border {$variantClass} {$sizeClass}"; @endphp @if($tag === 'a') merge(['class' => $classes]) }}> diff --git a/resources/views/components/data-table.blade.php b/resources/views/components/data-table.blade.php index 7a03fe7..cc5aa0c 100644 --- a/resources/views/components/data-table.blade.php +++ b/resources/views/components/data-table.blade.php @@ -1,17 +1,17 @@ @props(['id', 'columns', 'ajax', 'bulkDeleteUrl' => null]) @php -$dtColumns = collect($columns) - ->map(function ($c) { - return [ - 'data' => $c['key'], - 'name' => $c['key'], - 'orderable' => $c['orderable'] ?? true, - 'searchable' => $c['searchable'] ?? true, - 'className' => $c['className'] ?? '', - ]; - }) - ->values(); + $dtColumns = collect($columns) + ->map(function ($c) { + return [ + 'data' => $c['key'], + 'name' => $c['key'], + 'orderable' => $c['orderable'] ?? true, + 'searchable' => $c['searchable'] ?? true, + 'className' => $c['className'] ?? '', + ]; + }) + ->values(); @endphp @if ($bulkDeleteUrl)
0 items selected
- + Delete Selected
@@ -58,14 +58,14 @@ $dtColumns = collect($columns)
@endif -
+
{{-- Loading Process Indicator --}} - @@ -102,8 +102,7 @@ $dtColumns = collect($columns) + placeholder="Filter..." data-column="{{ $loop->index }}" style="font-size: 0.8125rem;"> @endif @@ -116,473 +115,519 @@ $dtColumns = collect($columns)
@push('custom-scripts') - + // Re-render icons after reload + if (window.lucide) lucide.createIcons(); + }, + error: function (xhr) { + const message = xhr.responseJSON?.message || + 'Something went wrong.'; + if (typeof FuiToast !== 'undefined') { + FuiToast.error(message); + } else { + alert(message); + } + if (bsModal) bsModal.hide(); + }, + complete: function () { + confirmBtn.prop('disabled', false).text('Delete'); + } + }); + }); + }); + @endif + - -@endpush + .loading-bar::after { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 50%; + background: linear-gradient(90deg, transparent, #0f172a, transparent); + animation: loading-process-info 1.5s infinite linear; + } + + .loading-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.5); + backdrop-filter: blur(1px); + } + + @keyframes loading-process-info { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(200%); + } + } + + /* Custom Sort Icons Styles */ + .sort-icons { + opacity: 0.15; + transition: opacity 0.2s; + } + + th:hover .sort-icons { + opacity: 0.5; + } + + th.sorting_asc .sort-icons { + opacity: 1; + } + + th.sorting_asc .sort-icon-desc { + display: none !important; + } + + th.sorting_desc .sort-icons { + opacity: 1; + } + + th.sorting_desc .sort-icon-asc { + display: none !important; + } + + /* Hide default DataTables icons */ + table.dataTable thead .sorting:before, + table.dataTable thead .sorting:after, + table.dataTable thead .sorting_asc:before, + table.dataTable thead .sorting_asc:after, + table.dataTable thead .sorting_desc:before, + table.dataTable thead .sorting_desc:after { + display: none !important; + } + + /* Processing Indicator with improved look - Centered */ + div.dataTables_processing { + display: none !important; + /* Hide default processing */ + } + +@endpush \ No newline at end of file