feat: add bulk delete with validation to IP Pool and update provisioning logic and email notifications
This commit is contained in:
@@ -5,39 +5,46 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thông báo đơn hàng mới</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; padding: 0; }
|
||||
.wrapper { max-width: 640px; margin: 30px auto; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
||||
.header { background: #1e40af; padding: 24px 32px; }
|
||||
.header h1 { color: #fff; margin: 0; font-size: 20px; }
|
||||
.header span { color: #bfdbfe; font-size: 13px; }
|
||||
.body { padding: 32px; color: #374151; }
|
||||
.body p { line-height: 1.7; margin: 0 0 12px; }
|
||||
.section-title { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: #6b7280; margin: 24px 0 10px; }
|
||||
.info-box { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 6px; padding: 16px 20px; }
|
||||
.info-box table { width: 100%; border-collapse: collapse; }
|
||||
.info-box td { padding: 5px 0; font-size: 14px; vertical-align: top; }
|
||||
.info-box td:first-child { color: #6b7280; width: 160px; white-space: nowrap; }
|
||||
.info-box td:last-child { font-weight: 600; color: #111827; }
|
||||
* { box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', Arial, sans-serif; background: #f0f4f8; margin: 0; padding: 0; }
|
||||
.wrapper { max-width: 620px; margin: 30px auto; }
|
||||
.card { background: #fff; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
|
||||
.header { background: linear-gradient(135deg, #1e40af 0%, #1d4ed8 100%); padding: 36px 40px; text-align: center; }
|
||||
.header .icon { font-size: 48px; margin-bottom: 12px; }
|
||||
.header h1 { color: #fff; margin: 0 0 6px; font-size: 22px; font-weight: 700; }
|
||||
.header p { color: #bfdbfe; margin: 0; font-size: 14px; }
|
||||
.body { padding: 36px 40px; }
|
||||
.section-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #9ca3af; margin: 24px 0 10px; }
|
||||
.section-label:first-child { margin-top: 0; }
|
||||
.info-table { width: 100%; border-collapse: collapse; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; }
|
||||
.info-table td { padding: 10px 16px; font-size: 14px; border-bottom: 1px solid #f3f4f6; }
|
||||
.info-table tr:last-child td { border-bottom: none; }
|
||||
.info-table td:first-child { color: #6b7280; width: 45%; }
|
||||
.info-table td:last-child { font-weight: 600; color: #111827; }
|
||||
.badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
|
||||
.badge-blue { background: #dbeafe; color: #1e40af; }
|
||||
.badge-green { background: #dcfce7; color: #15803d; }
|
||||
.divider { border: none; border-top: 1px solid #e5e7eb; margin: 24px 0; }
|
||||
.footer { background: #f9fafb; border-top: 1px solid #e5e7eb; padding: 16px 32px; text-align: center; font-size: 12px; color: #9ca3af; }
|
||||
.badge-blue { background: #dbeafe; color: #1e40af; }
|
||||
.amount-row td:last-child { font-size: 16px; font-weight: 800; color: #1e40af; }
|
||||
.vm-box { background: #f0fdf4; border: 1.5px solid #86efac; border-radius: 10px; padding: 20px 24px; margin-top: 10px; }
|
||||
.vm-row { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid #dcfce7; }
|
||||
.vm-row:last-child { border-bottom: none; }
|
||||
.vm-label { color: #15803d; font-size: 13px; width: 130px; flex-shrink: 0; }
|
||||
.vm-value { font-family: 'Courier New', monospace; font-size: 14px; font-weight: 700; color: #111827; word-break: break-all; }
|
||||
.footer { text-align: center; padding: 20px 40px; border-top: 1px solid #f3f4f6; font-size: 12px; color: #9ca3af; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<h1>📦 Đơn hàng mới đã được thanh toán</h1>
|
||||
<span>Thông báo tự động từ hệ thống quản lý</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<div class="icon">📦</div>
|
||||
<h1>Đơn hàng mới đã thanh toán</h1>
|
||||
<p>Đơn #{{ $order_num ?? 'N/A' }} — {{ $created_at ?? now()->format('d/m/Y H:i:s') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<p>Một đơn hàng mới vừa được khách hàng tạo và thanh toán thành công trên hệ thống.</p>
|
||||
|
||||
<div class="section-title">Thông tin khách hàng</div>
|
||||
<div class="info-box">
|
||||
<table>
|
||||
<div class="body">
|
||||
<div class="section-label">Thông tin khách hàng</div>
|
||||
<table class="info-table">
|
||||
<tr>
|
||||
<td>Tên khách hàng</td>
|
||||
<td>{{ $customer_name ?? 'N/A' }}</td>
|
||||
@@ -46,16 +53,16 @@
|
||||
<td>Email</td>
|
||||
<td>{{ $customer_email ?? 'N/A' }}</td>
|
||||
</tr>
|
||||
@if(!empty($client_ip))
|
||||
<tr>
|
||||
<td>IP đặt hàng</td>
|
||||
<td>{{ $client_ip ?? 'N/A' }}</td>
|
||||
<td>{{ $client_ip }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section-title">Thông tin đơn hàng</div>
|
||||
<div class="info-box">
|
||||
<table>
|
||||
<div class="section-label">Thông tin đơn hàng</div>
|
||||
<table class="info-table">
|
||||
<tr>
|
||||
<td>Mã đơn hàng</td>
|
||||
<td>#{{ $order_num ?? 'N/A' }}</td>
|
||||
@@ -72,55 +79,45 @@
|
||||
<td>Chu kỳ thanh toán</td>
|
||||
<td>{{ $billing_cycle ?? 'N/A' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr class="amount-row">
|
||||
<td>Tổng tiền</td>
|
||||
<td><strong>{{ number_format($total ?? 0, 0, ',', '.') }} VNĐ</strong></td>
|
||||
<td>{{ number_format($total ?? 0, 0, ',', '.') }} VNĐ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Trạng thái</td>
|
||||
<td><span class="badge badge-green">Đã thanh toán</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Thời gian</td>
|
||||
<td>{{ $created_at ?? now()->format('d/m/Y H:i:s') }}</td>
|
||||
<td><span class="badge badge-green">✅ Đã thanh toán</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if(!empty($username) || !empty($password) || !empty($ip))
|
||||
<div class="section-title">Thông tin dịch vụ</div>
|
||||
<div class="info-box">
|
||||
<table>
|
||||
@if(!empty($ip) || !empty($username) || !empty($password))
|
||||
<div class="section-label">Thông tin dịch vụ Cloud VPS</div>
|
||||
<div class="vm-box">
|
||||
@if(!empty($ip))
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">🌐 IP Address</span>
|
||||
<span class="vm-value">{{ $ip }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($username))
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td>{{ $username }}</td>
|
||||
</tr>
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">👤 Username</span>
|
||||
<span class="vm-value">{{ $username }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($password))
|
||||
<tr>
|
||||
<td>Password</td>
|
||||
<td>{{ $password }}</td>
|
||||
</tr>
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">🔑 Password</span>
|
||||
<span class="vm-value">{{ $password }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($ip))
|
||||
<tr>
|
||||
<td>IP Address</td>
|
||||
<td>{{ $ip }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<hr class="divider">
|
||||
<p style="font-size:13px;color:#6b7280;">
|
||||
Email này được gửi tự động bởi hệ thống. Vui lòng không trả lời email này.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
© {{ date('Y') }} {{ $company_name ?? 'Portal Admin' }} — Hệ thống quản lý dịch vụ
|
||||
<div class="footer">
|
||||
© {{ date('Y') }} {{ $company_name ?? 'Portal Admin' }} — Hệ thống quản lý dịch vụ<br>
|
||||
Email này được gửi tự động. Vui lòng không trả lời.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="vi">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Đặt hàng thành công</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', Arial, sans-serif; background: #f0f4f8; margin: 0; padding: 0; }
|
||||
.wrapper { max-width: 620px; margin: 30px auto; }
|
||||
.card { background: #fff; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
|
||||
.header { background: linear-gradient(135deg, #16a34a 0%, #15803d 100%); padding: 36px 40px; text-align: center; }
|
||||
.header .icon { font-size: 48px; margin-bottom: 12px; }
|
||||
.header h1 { color: #fff; margin: 0 0 6px; font-size: 24px; font-weight: 700; }
|
||||
.header p { color: #bbf7d0; margin: 0; font-size: 14px; }
|
||||
.body { padding: 36px 40px; }
|
||||
.greeting { font-size: 16px; color: #374151; margin-bottom: 20px; line-height: 1.6; }
|
||||
.section-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #9ca3af; margin: 28px 0 10px; }
|
||||
.info-table { width: 100%; border-collapse: collapse; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; }
|
||||
.info-table td { padding: 10px 16px; font-size: 14px; border-bottom: 1px solid #f3f4f6; }
|
||||
.info-table tr:last-child td { border-bottom: none; }
|
||||
.info-table td:first-child { color: #6b7280; width: 45%; }
|
||||
.info-table td:last-child { font-weight: 600; color: #111827; }
|
||||
.vm-box { background: #f0fdf4; border: 1.5px solid #86efac; border-radius: 10px; padding: 20px 24px; margin-top: 12px; }
|
||||
.vm-box .vm-row { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid #dcfce7; }
|
||||
.vm-box .vm-row:last-child { border-bottom: none; }
|
||||
.vm-box .vm-label { color: #15803d; font-size: 13px; width: 120px; flex-shrink: 0; }
|
||||
.vm-box .vm-value { font-family: 'Courier New', monospace; font-size: 14px; font-weight: 700; color: #111827; word-break: break-all; }
|
||||
.amount-box { background: #fffbeb; border: 1.5px solid #fbbf24; border-radius: 8px; padding: 14px 20px; display: flex; justify-content: space-between; align-items: center; margin-top: 20px; }
|
||||
.amount-box span { font-size: 13px; color: #92400e; }
|
||||
.amount-box strong { font-size: 20px; font-weight: 800; color: #b45309; }
|
||||
.notice { background: #eff6ff; border-left: 3px solid #3b82f6; padding: 12px 16px; border-radius: 0 6px 6px 0; font-size: 13px; color: #1e40af; margin-top: 24px; line-height: 1.6; }
|
||||
.footer { text-align: center; padding: 20px 40px; border-top: 1px solid #f3f4f6; font-size: 12px; color: #9ca3af; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<div class="icon">✅</div>
|
||||
<h1>Đặt hàng thành công!</h1>
|
||||
<p>Đơn hàng #{{ $orderNum ?? 'N/A' }} đã được xác nhận và thanh toán</p>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<p class="greeting">
|
||||
Xin chào <strong>{{ $customerName ?? 'Quý khách' }}</strong>,<br>
|
||||
Cảm ơn bạn đã tin tưởng sử dụng dịch vụ của chúng tôi. Đơn hàng của bạn đã được xác nhận và đang trong quá trình khởi tạo.
|
||||
</p>
|
||||
|
||||
<div class="section-label">Thông tin đơn hàng</div>
|
||||
<table class="info-table">
|
||||
<tr><td>Mã đơn hàng</td><td>#{{ $orderNum ?? 'N/A' }}</td></tr>
|
||||
<tr><td>Tổng thanh toán</td><td>{{ number_format($amount ?? 0, 0, ',', '.') }} VNĐ</td></tr>
|
||||
</table>
|
||||
|
||||
@if(!empty($vmResults))
|
||||
<div class="section-label">Thông tin dịch vụ Cloud VPS</div>
|
||||
@foreach($vmResults as $vm)
|
||||
<div class="vm-box">
|
||||
@if(!empty($vm['ip']))
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">🌐 IP Address</span>
|
||||
<span class="vm-value">{{ $vm['ip'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($vm['username']))
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">👤 Username</span>
|
||||
<span class="vm-value">{{ $vm['username'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($vm['password']))
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">🔑 Password</span>
|
||||
<span class="vm-value">{{ $vm['password'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($vm['os']))
|
||||
<div class="vm-row">
|
||||
<span class="vm-label">💿 Hệ điều hành</span>
|
||||
<span class="vm-value">{{ $vm['os'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
<div class="notice">
|
||||
⚠️ Vui lòng <strong>đổi mật khẩu ngay</strong> sau khi đăng nhập lần đầu để bảo mật tài khoản.<br>
|
||||
Nếu cần hỗ trợ, liên hệ: <strong>{{ $supportEmail ?? '' }}</strong> hoặc <strong>{{ $supportPhone ?? '' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
Email này được gửi tự động. Vui lòng không trả lời email này.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user