refactor: Replace Flux UI components with native HTML/Alpine.js and update Composer dependencies.

This commit is contained in:
pbao9
2026-03-04 09:13:15 +07:00
parent db3a8c4949
commit 68540009c0
6 changed files with 200 additions and 232 deletions
+1 -1
View File
@@ -234,7 +234,7 @@ class VPSReinstallModal extends Component
if ($response->successful()) {
session()->flash('success', __('Yêu cầu cài lại hệ điều hành đã được gửi thành công.'));
$this->dispatch('close-modal', ['name' => 'reinstall-vm']);
$this->dispatch('close-reinstall-modal');
} else {
$errorMessage = $response->json('message') ?? __('Có lỗi xảy ra khi gửi yêu cầu.');
session()->flash('error', $errorMessage);
-1
View File
@@ -21,7 +21,6 @@
"laravel/passport": "^12.2",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.7",
"livewire/flux": "^2.2",
"livewire/livewire": "^3.6",
"maatwebsite/excel": "^3.1",
"opcodesio/log-viewer": "^3.18",
Generated
+1 -67
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e8e3d079473d0f39c2ea9336da6e56cc",
"content-hash": "ea9197c6a2804717c1efcd340c4b7828",
"packages": [
{
"name": "aws/aws-crt-php",
@@ -3921,72 +3921,6 @@
],
"time": "2026-01-15T06:54:53+00:00"
},
{
"name": "livewire/flux",
"version": "v2.11.1",
"source": {
"type": "git",
"url": "https://github.com/livewire/flux.git",
"reference": "3ada3b2644215fd1ccb7003ce8e6bc185c22e70a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/flux/zipball/3ada3b2644215fd1ccb7003ce8e6bc185c22e70a",
"reference": "3ada3b2644215fd1ccb7003ce8e6bc185c22e70a",
"shasum": ""
},
"require": {
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/view": "^10.0|^11.0|^12.0",
"laravel/prompts": "^0.1|^0.2|^0.3",
"livewire/livewire": "^3.7.4|^4.0",
"php": "^8.1",
"symfony/console": "^6.0|^7.0"
},
"conflict": {
"livewire/blaze": "<1.0.0"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Flux": "Flux\\Flux"
},
"providers": [
"Flux\\FluxServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Flux\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"proprietary"
],
"authors": [
{
"name": "Caleb Porzio",
"email": "calebporzio@gmail.com"
}
],
"description": "The official UI component library for Livewire.",
"keywords": [
"components",
"flux",
"laravel",
"livewire",
"ui"
],
"support": {
"issues": "https://github.com/livewire/flux/issues",
"source": "https://github.com/livewire/flux/tree/v2.11.1"
},
"time": "2026-01-21T17:09:56+00:00"
},
{
"name": "livewire/livewire",
"version": "v3.7.6",
@@ -5,5 +5,4 @@
@push('scriptsLivewire')
@livewireScripts
@fluxScripts
@endpush
@@ -201,7 +201,10 @@
<!-- Gia hạn tự động -->
<td class="text-center py-3 px-4">
<flux:switch wire:model.live="auto_renew" :checked="$invoice->auto_renew" />
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" wire:model.live="auto_renew" @checked($invoice->auto_renew) class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:bg-blue-600 after:content-[''] after:absolute after:top-0.5 after:left-0.5 after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:after:translate-x-full"></div>
</label>
</td>
<!-- Thao tác -->
@@ -1,174 +1,207 @@
<flux:modal name="reinstall-vm" class="md:w-[600px]">
<div class="space-y-6">
<div>
<flux:heading size="lg">{{ __('Chọn lại Hệ điều hành') }}</flux:heading>
<p class="text-sm text-gray-600 mt-2">{{ __('Chọn hệ điều hành mới cho máy ảo của bạn.') }}</p>
</div>
<div
x-data="{ open: false }"
x-on:open-reinstall-modal.window="open = true"
x-on:close-reinstall-modal.window="open = false"
>
{{-- Backdrop --}}
<div
x-show="open"
x-transition.opacity
class="fixed inset-0 bg-black/50 z-40"
x-on:click="open = false"
></div>
@if ($currentOs)
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div class="flex items-center">
<svg class="w-5 h-5 text-blue-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div>
<p class="text-sm font-medium text-blue-800">{{ __('Hệ điều hành hiện tại:') }}</p>
<p class="text-sm text-blue-700">
@if ($currentOs && $currentOs->osGroup)
{{ $currentOs->osGroup->name }} - {{ $currentOs->version ?? 'N/A' }}
@else
{{ __('Không xác định') }}
@endif
</p>
</div>
</div>
</div>
@endif
@if (count($osGroups) > 0)
<div class="space-y-4">
{{-- Modal panel --}}
<div
x-show="open"
x-transition
class="fixed inset-0 z-50 flex items-center justify-center p-4"
>
<div class="bg-white rounded-lg shadow-xl md:w-[600px] w-full max-h-[90vh] overflow-y-auto">
<div class="p-6 space-y-6">
<div>
<label
class="block text-sm font-medium text-gray-700 mb-2">{{ __('Chọn loại hệ điều hành') }}</label>
<flux:select wire:model.live="selectedOsGroupId" placeholder="{{ __('Chọn loại hệ điều hành') }}"
:disabled="$isLoading">
<flux:select.option value="">{{ __('Chọn loại hệ điều hành') }}</flux:select.option>
@foreach ($osGroups as $osGroup)
<flux:select.option value="{{ $osGroup->id }}">{{ $osGroup->name }}
</flux:select.option>
@endforeach
</flux:select>
<h2 class="text-lg font-semibold text-gray-900">{{ __('Chọn lại Hệ điều hành') }}</h2>
<p class="text-sm text-gray-600 mt-2">{{ __('Chọn hệ điều hành mới cho máy ảo của bạn.') }}</p>
</div>
@if ($selectedOsGroupId)
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
{{ __('Chọn phiên bản:') }}
@if ($isLoading)
<span class="text-sm text-gray-500 ml-2">
<svg class="inline w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
{{ __('Đang tải...') }}
</span>
@endif
</label>
@if ($isLoading)
<div class="text-center py-8">
<svg class="mx-auto h-8 w-8 text-gray-400 animate-spin" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
<p class="mt-2 text-sm text-gray-500">{{ __('Đang tải danh sách phiên bản...') }}</p>
</div>
@elseif (count($operatingSystems) > 0)
<div class="grid grid-cols-1 gap-3 max-h-60 overflow-y-auto">
@foreach ($operatingSystems as $os)
<label
class="flex items-center p-3 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<input type="radio" wire:model.live="selectedOsId" value="{{ $os->id }}"
class="mr-3">
<div class="flex-1">
<div class="font-medium text-gray-900">
{{ $os->osGroup->name . ' - ' . $os->version }}
</div>
@if ($os->description)
<div class="text-sm text-gray-500 mt-1">
{{ $os->description }}
</div>
@endif
</div>
<div class="flex items-center space-x-2">
@if ($currentOs && $currentOs->id == $os->id)
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
{{ __('Hiện tại') }}
</span>
@endif
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{{ __('Khả dụng') }}
</span>
</div>
</label>
@endforeach
</div>
@else
<div class="text-center py-8">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.47-.881-6.08-2.33" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">{{ __('Không có phiên bản nào') }}
</h3>
<p class="mt-1 text-sm text-gray-500">
{{ __('Không có phiên bản hệ điều hành nào khả dụng cho loại này.') }}</p>
</div>
@endif
</div>
@endif
@if ($selectedOsId && $selectedOsDetails && $vpsInfo)
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mt-4">
<div class="flex items-start">
<svg class="w-5 h-5 text-yellow-600 mr-2 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
@if ($currentOs)
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div class="flex items-center">
<svg class="w-5 h-5 text-blue-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div class="flex-1">
<h3 class="text-sm font-medium text-yellow-800 mb-2">{{ __('Quý khách cài đặt lại VPS với thông tin như sau:') }}</h3>
<div class="text-sm text-yellow-700 space-y-1">
<p><strong>{{ __('VPS:') }}</strong> {{ $vpsIpAddress ?? $moduleVmware?->name ?? $vpsInfo->name ?? 'N/A' }}</p>
<p><strong>{{ __('Hệ điều hành:') }}</strong> {{ $selectedOsDetails->osGroup->name ?? '' }} {{ $selectedOsDetails->version ?? '' }}</p>
</div>
<div class="mt-3 p-3 bg-yellow-100 rounded border border-yellow-300">
<p class="text-sm text-yellow-800 font-medium">{{ __('Lưu ý:') }}</p>
<p class="text-sm text-yellow-700">{{ __('Hành động này rất nguy hiểm, nó có thể xóa VPS và cài đặt lại. Quý khách vui lòng kiểm tra lại các VPS cần cài đặt lại.') }}</p>
</div>
<div>
<p class="text-sm font-medium text-blue-800">{{ __('Hệ điều hành hiện tại:') }}</p>
<p class="text-sm text-blue-700">
@if ($currentOs && $currentOs->osGroup)
{{ $currentOs->osGroup->name }} - {{ $currentOs->version ?? 'N/A' }}
@else
{{ __('Không xác định') }}
@endif
</p>
</div>
</div>
</div>
@endif
<div class="flex justify-end space-x-3 pt-4 border-t border-gray-200">
<flux:button variant="primary" x-on:click="$flux.modal('reinstall-vm').close()" :disabled="$isLoading">
{{ __('Hủy') }}
</flux:button>
@if ($selectedOsId && $selectedOsDetails)
<flux:button variant="primary" color="orange" wire:click="confirmReinstall" :disabled="$isLoading">
@if ($isLoading)
<svg class="w-4 h-4 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
{{ __('Đang xử lý...') }}
@else
{{ __('Xác nhận cài lại') }}
@if (count($osGroups) > 0)
<div class="space-y-4">
<div>
<label
class="block text-sm font-medium text-gray-700 mb-2">{{ __('Chọn loại hệ điều hành') }}</label>
<select
wire:model.live="selectedOsGroupId"
:disabled="$isLoading"
class="w-full py-2 px-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
<option value="">{{ __('Chọn loại hệ điều hành') }}</option>
@foreach ($osGroups as $osGroup)
<option value="{{ $osGroup->id }}">{{ $osGroup->name }}</option>
@endforeach
</select>
</div>
@if ($selectedOsGroupId)
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
{{ __('Chọn phiên bản:') }}
@if ($isLoading)
<span class="text-sm text-gray-500 ml-2">
<svg class="inline w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
{{ __('Đang tải...') }}
</span>
@endif
</label>
@if ($isLoading)
<div class="text-center py-8">
<svg class="mx-auto h-8 w-8 text-gray-400 animate-spin" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
<p class="mt-2 text-sm text-gray-500">{{ __('Đang tải danh sách phiên bản...') }}</p>
</div>
@elseif (count($operatingSystems) > 0)
<div class="grid grid-cols-1 gap-3 max-h-60 overflow-y-auto">
@foreach ($operatingSystems as $os)
<label
class="flex items-center p-3 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors">
<input type="radio" wire:model.live="selectedOsId" value="{{ $os->id }}"
class="mr-3">
<div class="flex-1">
<div class="font-medium text-gray-900">
{{ $os->osGroup->name . ' - ' . $os->version }}
</div>
@if ($os->description)
<div class="text-sm text-gray-500 mt-1">
{{ $os->description }}
</div>
@endif
</div>
<div class="flex items-center space-x-2">
@if ($currentOs && $currentOs->id == $os->id)
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
{{ __('Hiện tại') }}
</span>
@endif
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{{ __('Khả dụng') }}
</span>
</div>
</label>
@endforeach
</div>
@else
<div class="text-center py-8">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.47-.881-6.08-2.33" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">{{ __('Không có phiên bản nào') }}
</h3>
<p class="mt-1 text-sm text-gray-500">
{{ __('Không có phiên bản hệ điều hành nào khả dụng cho loại này.') }}</p>
</div>
@endif
</div>
@endif
@if ($selectedOsId && $selectedOsDetails && $vpsInfo)
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mt-4">
<div class="flex items-start">
<svg class="w-5 h-5 text-yellow-600 mr-2 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
<div class="flex-1">
<h3 class="text-sm font-medium text-yellow-800 mb-2">{{ __('Quý khách cài đặt lại VPS với thông tin như sau:') }}</h3>
<div class="text-sm text-yellow-700 space-y-1">
<p><strong>{{ __('VPS:') }}</strong> {{ $vpsIpAddress ?? $moduleVmware?->name ?? $vpsInfo->name ?? 'N/A' }}</p>
<p><strong>{{ __('Hệ điều hành:') }}</strong> {{ $selectedOsDetails->osGroup->name ?? '' }} {{ $selectedOsDetails->version ?? '' }}</p>
</div>
<div class="mt-3 p-3 bg-yellow-100 rounded border border-yellow-300">
<p class="text-sm text-yellow-800 font-medium">{{ __('Lưu ý:') }}</p>
<p class="text-sm text-yellow-700">{{ __('Hành động này rất nguy hiểm, nó có thể xóa VPS và cài đặt lại. Quý khách vui lòng kiểm tra lại các VPS cần cài đặt lại.') }}</p>
</div>
</div>
</div>
</div>
@endif
<div class="flex justify-end space-x-3 pt-4 border-t border-gray-200">
<button
type="button"
x-on:click="open = false"
:disabled="$isLoading"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
{{ __('Hủy') }}
</button>
@if ($selectedOsId && $selectedOsDetails)
<button
type="button"
wire:click="confirmReinstall"
:disabled="$isLoading"
class="px-4 py-2 text-sm font-medium text-white bg-orange-500 border border-transparent rounded-md hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
@if ($isLoading)
<svg class="w-4 h-4 mr-2 animate-spin inline" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
{{ __('Đang xử lý...') }}
@else
{{ __('Xác nhận cài lại') }}
@endif
</button>
@endif
</flux:button>
@endif
</div>
</div>
</div>
@else
<div class="text-center py-8">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.47-.881-6.08-2.33" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">{{ __('Không có hệ điều hành nào') }}</h3>
<p class="mt-1 text-sm text-gray-500">
{{ __('Không có hệ điều hành nào khả dụng cho VPS này.') }}</p>
</div>
@endif
</div>
@else
<div class="text-center py-8">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.47-.881-6.08-2.33" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">{{ __('Không có hệ điều hành nào') }}</h3>
<p class="mt-1 text-sm text-gray-500">
{{ __('Không có hệ điều hành nào khả dụng cho VPS này.') }}</p>
</div>
@endif
</div>
</div>
</flux:modal>
</div>