417 lines
26 KiB
PHP
417 lines
26 KiB
PHP
@extends('frontend.layouts.master')
|
|
|
|
@push('viewStyles')
|
|
<link href="{{ asset('css/service/dropdown.css') }}" rel="stylesheet">
|
|
<style>
|
|
.custom-dropdown {
|
|
position: relative;
|
|
width: 100%;
|
|
z-index: 1000;
|
|
}
|
|
.dropdown-selected {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 10px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
background: white;
|
|
cursor: pointer !important;
|
|
transition: all 0.2s ease;
|
|
font-weight: 500;
|
|
user-select: none;
|
|
position: relative;
|
|
z-index: 1001;
|
|
}
|
|
.dropdown-selected:hover {
|
|
border-color: #f66d2f;
|
|
box-shadow: 0 0 0 3px rgba(246, 109, 47, 0.1);
|
|
}
|
|
.dropdown-selected.active {
|
|
border-color: #f66d2f;
|
|
box-shadow: 0 0 0 3px rgba(246, 109, 47, 0.1);
|
|
}
|
|
.dropdown-options {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
background: white;
|
|
border: 2px solid #f66d2f;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
z-index: 1002;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
margin-top: 4px;
|
|
display: none;
|
|
}
|
|
.dropdown-options.show {
|
|
display: block !important;
|
|
}
|
|
.dropdown-option {
|
|
padding: 12px 16px;
|
|
cursor: pointer !important;
|
|
transition: all 0.2s ease;
|
|
border-bottom: 1px solid #f3f4f6;
|
|
user-select: none;
|
|
}
|
|
.dropdown-option:hover {
|
|
background: #fff5f0;
|
|
color: #f66d2f;
|
|
}
|
|
.dropdown-option.selected {
|
|
background: #f66d2f;
|
|
color: white;
|
|
}
|
|
.dropdown-option:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.dropdown-arrow {
|
|
transition: transform 0.2s ease;
|
|
color: #6b7280;
|
|
font-size: 12px;
|
|
}
|
|
.dropdown-selected.active .dropdown-arrow,
|
|
.dropdown-arrow.rotate-180 {
|
|
transform: rotate(180deg);
|
|
color: #f66d2f;
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<form action="{{ route('client.order.store') }}" method="post">
|
|
@csrf
|
|
<div class="container mx-auto mt-10 px-4">
|
|
@if ($cartItems && count($cartItems) > 0)
|
|
@php $total = 0; @endphp
|
|
|
|
<!-- Main Title -->
|
|
<div class="text-center mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-800">Cấu hình tùy chọn và tiếp tục thanh toán</h1>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8" x-data="{
|
|
paymentPeriod: {{ $cartItems[0]['months'] ?? 6 }},
|
|
baseTotal: {{ $cartItems[0]['product']->price_sale ?? $cartItems[0]['product']->price ?? 0 }},
|
|
get totalPrice() {
|
|
const originalPrice = this.baseTotal * this.paymentPeriod;
|
|
const discount = this.paymentPeriod >= 12 ? Math.round(originalPrice * 0.1) : 0;
|
|
return originalPrice - discount;
|
|
}
|
|
}">
|
|
<!-- Left Column - Product Configuration -->
|
|
<div class="space-y-6">
|
|
@foreach ($cartItems as $index => $item)
|
|
@php
|
|
$product = $item['product'];
|
|
$total += $item['price'];
|
|
$addons = $item['addon'] ?? [];
|
|
$osGroups = $item['os_group'] ?? [];
|
|
@endphp
|
|
<input type="hidden" name="cart_id" value="{{ $item['id'] }}">
|
|
<input type="hidden" name="months" value="{{ $item['months'] }}">
|
|
|
|
<!-- Domain Input Section -->
|
|
<div class="bg-white rounded-lg border border-gray-200 p-6">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-4">Domain</h3>
|
|
<div x-data="{ serverName: '', isValid: true }">
|
|
<flux:input name="server_name" placeholder="Domain" x-model="serverName"
|
|
@input="isValid = /^[a-zA-Z0-9.-]+$/.test(serverName)">
|
|
<x-slot name="iconTrailing">
|
|
<flux:button size="sm" variant="subtle" icon="x-mark" class="-mr-1"
|
|
@click="serverName = ''; isValid = true" type="button" />
|
|
</x-slot>
|
|
</flux:input>
|
|
|
|
<template x-if="!isValid">
|
|
<div class="p-4 my-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"
|
|
role="alert">
|
|
Tên server không hợp lệ. Chỉ được dùng chữ, số, dấu chấm và dấu gạch ngang.
|
|
<b>VD: tenmiencuaban.com.vn</b>
|
|
</div>
|
|
</template>
|
|
|
|
<p class="text-xs text-red-600 mt-2">
|
|
Lưu ý: Quý khách chỉ ghi tên Domain, không cần ghi đầy đủ tiền tố http:// hoặc
|
|
https://
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Product Title and Price -->
|
|
<div class="bg-white rounded-lg border border-gray-200 p-6">
|
|
<input type="hidden" name="product_id" value="{{ $product->id }}" />
|
|
<input type="hidden" name="product_type" value="{{ $item['product_type'] }}" />
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h2 class="text-xl font-semibold text-gray-800 border-b-2 border-blue-500 pb-2">
|
|
{{ $product->name }}
|
|
@if ($item['product_type'] == 4)
|
|
<span class="text-sm text-gray-500">(VPS)</span>
|
|
@elseif ($item['product_type'] == 3)
|
|
<span class="text-sm text-gray-500">(Hosting)</span>
|
|
@endif
|
|
</h2>
|
|
<div class="text-right">
|
|
@if ($product->price_sale && $product->price_sale < $product->price)
|
|
<div class="text-lg font-bold text-red-600">
|
|
{{ format_price($product->price_sale) }}/tháng
|
|
</div>
|
|
<div class="text-sm text-gray-400 line-through">
|
|
{{ format_price($product->price) }}/tháng
|
|
</div>
|
|
@else
|
|
<div class="text-lg font-bold text-gray-800">
|
|
{{ format_price($product->price) }}/tháng
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Product Features -->
|
|
<div class="space-y-2 text-sm text-gray-700">
|
|
@if ($item['product_type'] == 3)
|
|
<!-- Hosting -->
|
|
<div>• {{ number_format($product->disk ?? 2000) }} MB dung lượng SSD.</div>
|
|
<div>• 1 Addon domain.</div>
|
|
<div>• 10 Sub-domains.</div>
|
|
<div>• Không giới hạn Alias Domain.</div>
|
|
<div>• Không giới hạn Database.</div>
|
|
<div>• Không giới hạn tài khoản FTP.</div>
|
|
<div>• Control Panel: Cpanel.</div>
|
|
@else
|
|
<!-- VPS Features -->
|
|
<div
|
|
class="flex items-center space-x-3 p-3 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-lg border border-blue-100">
|
|
<div class="flex-shrink-0">
|
|
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z">
|
|
</path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-blue-800">{{ $product->cpu ?? 'Không rõ' }}
|
|
vCore</div>
|
|
<div class="text-xs text-blue-600">CPU</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center space-x-3 p-3 bg-gradient-to-r from-green-50 to-emerald-50 rounded-lg border border-green-100">
|
|
<div class="flex-shrink-0">
|
|
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10">
|
|
</path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-green-800">{{ $product->ram ?? 'Không rõ' }}
|
|
GB</div>
|
|
<div class="text-xs text-green-600">RAM</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center space-x-3 p-3 bg-gradient-to-r from-purple-50 to-violet-50 rounded-lg border border-purple-100">
|
|
<div class="flex-shrink-0">
|
|
<svg class="w-5 h-5 text-purple-600" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4">
|
|
</path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-purple-800">
|
|
{{ $product->disk ?? 'Không rõ' }} GB</div>
|
|
<div class="text-xs text-purple-600">SSD Storage</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center space-x-3 p-3 bg-gradient-to-r from-orange-50 to-amber-50 rounded-lg border border-orange-100">
|
|
<div class="flex-shrink-0">
|
|
<svg class="w-5 h-5 text-orange-600" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z">
|
|
</path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-orange-800">{{ $product->gpu ?? 'Không' }}
|
|
GB</div>
|
|
<div class="text-xs text-orange-600">GPU</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center space-x-3 p-3 bg-gradient-to-r from-teal-50 to-cyan-50 rounded-lg border border-teal-100">
|
|
<div class="flex-shrink-0">
|
|
<svg class="w-5 h-5 text-teal-600" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9v-9m0-9v9"></path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-teal-800">{{ $product->ip ?? '1' }}</div>
|
|
<div class="text-xs text-teal-600">IP Public</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="flex items-center space-x-3 p-3 bg-gradient-to-r from-rose-50 to-pink-50 rounded-lg border border-rose-100">
|
|
<div class="flex-shrink-0">
|
|
<svg class="w-5 h-5 text-rose-600" fill="none" stroke="currentColor"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-rose-800">
|
|
{{ $product->bandwidth ?? 'Không giới hạn' }}</div>
|
|
<div class="text-xs text-rose-600">Băng thông</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- OS Selection for VPS -->
|
|
@if ($item['product_type'] == 4 && isset($item['os_group']) && count($item['os_group']) > 0)
|
|
<div class="bg-white rounded-lg border border-gray-200 p-6" x-cloak
|
|
x-data="{ selectedOs: null, showOsDetails: false }">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-4">Hệ điều hành</h3>
|
|
<div class="space-y-4">
|
|
@foreach ($item['os_group'] as $group)
|
|
<div>
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<img src="{{ $group->thumbnail }}" alt="{{ $group->name }}"
|
|
class="w-6 h-6 rounded" />
|
|
<span class="font-medium text-gray-700">{{ $group->name }}</span>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
|
@foreach ($group->os as $os)
|
|
<div class="relative">
|
|
<button type="button"
|
|
@click="selectedOs = {{ $os->id }}; $dispatch('os-changed', {{ $os->id }}); showOsDetails = true"
|
|
:class="{
|
|
'border-orange-500 ring-2 ring-orange-400 bg-orange-50 text-orange-700': selectedOs ===
|
|
{{ $os->id }},
|
|
'border-gray-200 bg-white text-gray-700 hover:bg-gray-50': selectedOs !==
|
|
{{ $os->id }}
|
|
}"
|
|
class="border rounded-xl p-3 text-sm font-medium transition-all text-left flex items-center gap-3 w-full relative">
|
|
|
|
<img src="{{ $os->thumbnail ?? $group->thumbnail }}"
|
|
alt="{{ $os->version }}"
|
|
class="w-8 h-8 rounded object-cover" />
|
|
|
|
<div>
|
|
<div class="font-semibold">{{ $group->name }}</div>
|
|
<div class="text-xs text-gray-500">Version:
|
|
{{ $os->version }}</div>
|
|
</div>
|
|
|
|
<div x-show="selectedOs === {{ $os->id }}"
|
|
class="absolute top-1 right-1 bg-orange-500 text-white rounded-full p-0.5">
|
|
<svg class="w-4 h-4" fill="none"
|
|
stroke="currentColor" stroke-width="2"
|
|
viewBox="0 0 24 24">
|
|
<path stroke-linecap="round"
|
|
stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<!-- OS Details Panel -->
|
|
<div x-show="showOsDetails && selectedOs"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0 transform scale-95"
|
|
x-transition:enter-end="opacity-100 transform scale-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100 transform scale-100"
|
|
x-transition:leave-end="opacity-0 transform scale-95"
|
|
class="mt-4 p-4 bg-orange-50 rounded-lg border border-orange-200">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h5 class="font-semibold text-orange-800">Thông tin hệ điều hành</h5>
|
|
<flux:button size="sm" variant="subtle" icon="x-mark"
|
|
@click="showOsDetails = false" type="button" />
|
|
</div>
|
|
<p class="text-sm text-orange-700">
|
|
Bạn đã chọn hệ điều hành. Hệ điều hành sẽ được cài đặt tự động khi máy chủ được
|
|
tạo.
|
|
</p>
|
|
</div>
|
|
|
|
<input type="hidden" name="os_id" x-bind:value="selectedOs"
|
|
wire:model.defer="selectedOsList.{{ $index }}">
|
|
</div>
|
|
@endif
|
|
@endforeach
|
|
</div>
|
|
|
|
<!-- Right Column - Payment Summary -->
|
|
<div class="space-y-6">
|
|
|
|
<!-- Payment Period & Addons Section -->
|
|
<livewire:cart.summary-addons :product="$item['product']" :addons="$item['addon'] ?? []" :index="$index"
|
|
:months="$item['months']" />
|
|
|
|
|
|
<!-- Order Button -->
|
|
<div class="bg-white rounded-lg border border-gray-200 p-6">
|
|
<flux:button type="submit" variant="primary" color="orange" icon="credit-card"
|
|
class="w-full text-lg py-4">
|
|
Đặt hàng
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="text-center py-16">
|
|
<div class="w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
<svg class="w-12 h-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M3 3h2l.4 2M7 13h10l4-8H5.4m0 0L7 13m0 0l-2.5 5M7 13l2.5 5m6-5v6a2 2 0 01-2 2H9a2 2 0 01-2-2v-6m8 0V9a2 2 0 00-2-2H9a2 2 0 00-2 2v4.01">
|
|
</path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-xl font-semibold text-gray-800 mb-2">Giỏ hàng trống</h3>
|
|
<p class="text-gray-600 mb-6">Bạn chưa có sản phẩm nào trong giỏ hàng.</p>
|
|
<a href="{{ route('shop') }}"
|
|
class="inline-flex items-center px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition-colors">
|
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
</svg>
|
|
Mua sản phẩm
|
|
</a>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</form>
|
|
@endsection
|
|
|
|
@push('viewScripts')
|
|
<script>
|
|
// Alpine.js is now handling all dropdown functionality
|
|
console.log('Alpine.js dropdown initialized');
|
|
</script>
|
|
@endpush
|