Files
portal-api/resources/views/frontend/service/partials/renewal-modal.blade.php
T
2026-01-27 17:24:48 +07:00

158 lines
7.6 KiB
PHP

@props(['invoice'])
@php
$initialPrice = (int) ($invoice->invoiceItem->first()?->getProductAttribute()?->price_sale ?? $invoice->invoiceItem->first()?->getProductAttribute()?->price ?? 0);
@endphp
<flux:modal name="renewal-modal" class="md:w-[28rem]">
<div class="space-y-6" x-data="{
submitting: false,
plan: '12',
months: 12,
unitPrice: {{ $initialPrice }},
total: {{ $initialPrice * 12 }},
loading: false,
updateTotal() {
// Immediate client-side update for responsiveness
let m = (this.plan === 'other') ? (parseInt(this.months) || 0) : parseInt(this.plan);
if (m > 0) {
this.total = this.unitPrice * m;
}
},
async fetchPrice() {
this.loading = true;
try {
// Determine months based on plan
let m = (this.plan === 'other') ? (parseInt(this.months) || 1) : parseInt(this.plan);
if (m < 2 && this.plan === 'other') m = 2;
if (m < 1 && this.plan !== 'other') m = parseInt(this.plan);
// Optimistic UI update
if (this.plan !== 'other') this.months = m;
this.updateTotal();
const response = await fetch('{{ route('service.renew.price') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
product_id: '{{ $invoice->vm_id }}',
plan: this.plan,
months: m
})
});
const data = await response.json();
if (!data.error) {
this.unitPrice = data.data.unit_price;
this.total = data.data.total;
// Ensure months sync
if (this.plan !== 'other') {
this.months = m;
}
} else {
console.error('Renew Price Error:', data.message);
}
} catch (e) {
console.error('Fetch Error:', e);
} finally {
this.loading = false;
}
},
init() {
// Fetch fresh price on load to be sure
this.fetchPrice();
this.$watch('plan', (value) => {
console.log('Plan changed:', value);
this.fetchPrice();
});
this.$watch('months', (value) => {
if (this.plan === 'other') {
if (value >= 2) {
// Debounce slightly or just fetch
this.fetchPrice();
} else {
this.updateTotal(); // Just update math locally
}
}
});
}
}">
<div>
<flux:heading size="lg">{{ __('Gia hạn dịch vụ') }}</flux:heading>
<flux:text class="mt-2">{{ __('Chọn thời hạn gia hạn hoặc nhập số tháng mong muốn (Tối thiểu 2 tháng).') }}
</flux:text>
</div>
<form x-on:submit.prevent="if (submitting) return; submitting = true; $el.submit()" method="POST"
action="{{ route('service.renew') }}">
@csrf
<input type="hidden" name="product_id" value="{{ $invoice->vm_id }}">
<flux:field>
<flux:label>{{ __('Chọn thời hạn') }}</flux:label>
<flux:select x-model="plan" name="plan" x-on:change="fetchPrice()">
{{-- <flux:select.option value="1">{{ __('1 tháng') }}</flux:select.option> --}}
<!-- Min 2 months required -->
<flux:select.option value="3">{{ __('3 tháng') }}</flux:select.option>
<flux:select.option value="6">{{ __('6 tháng') }}</flux:select.option>
<flux:select.option value="12">{{ __('12 tháng') }}</flux:select.option>
<flux:select.option value="24">{{ __('24 tháng') }}</flux:select.option>
<flux:select.option value="other">{{ __('Khác...') }}</flux:select.option>
</flux:select>
</flux:field>
<div x-show="plan === 'other'" class="mt-2" x-transition>
<flux:field>
<flux:label>{{ __('Số tháng') }}</flux:label>
<flux:input x-model.number="months" name="months" type="number" min="2" max="60"
placeholder="{{ __('Nhập số tháng') }}" />
<flux:description>{{ __('Nhập số tháng muốn gia hạn (tối thiểu 2, tối đa 60).') }}</flux:description>
</flux:field>
</div>
<div class="mt-4 p-4 bg-gray-50 rounded-lg border border-gray-200 text-sm text-gray-700 relative">
<div x-show="loading" class="absolute inset-0 bg-white/50 flex items-center justify-center z-10 backdrop-blur-sm">
<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-orange-500"></div>
</div>
<div class="flex justify-between items-center mb-2">
<span class="text-gray-500">{{ __('Đơn giá / tháng') }}</span>
<span class="font-medium"
x-text="new Intl.NumberFormat('vi-VN', { style: 'currency', currency: 'VND' }).format(unitPrice)"></span>
</div>
<div class="border-t border-dashed border-gray-300 my-2"></div>
<div class="flex justify-between items-center text-base">
<span class="font-semibold text-gray-800">{{ __('Tổng thanh toán') }}</span>
<span class="font-bold text-orange-600 text-lg"
x-text="new Intl.NumberFormat('vi-VN', { style: 'currency', currency: 'VND' }).format(total)"></span>
</div>
<div class="mt-2 text-xs text-gray-400 text-right italic">
{{ __('(Đã bao gồm VAT nếu có)') }}
</div>
</div>
<div class="flex gap-2 justify-end mt-6">
<flux:modal.close>
<flux:button variant="ghost">{{ __('Hủy') }}</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="primary" color="orange"
x-bind:disabled="submitting || loading || (plan === 'other' && months < 2)">
<span x-show="!submitting">{{ __('Thanh toán gia hạn') }}</span>
<span x-show="submitting">{{ __('Đang xử lý...') }}</span>
</flux:button>
</div>
</form>
</div>
</flux:modal>