Add ServiceController for managing customer services; implement index, show, and detachCustomer methods

This commit is contained in:
baodev0
2026-07-14 10:14:50 +07:00
parent a282d38170
commit 9cae94b807
3 changed files with 245 additions and 62 deletions
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers\Api\Admin\Service;
use App\Http\Controllers\Controller;
use App\Models\Service;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ServiceController extends Controller
{
public function index(Request $request): JsonResponse
{
$perPage = min((int) ($request->per_page ?? 15), 100);
$query = Service::with(['customer:id,uuid,fullname,email', 'product:id,uuid,name', 'invoice:id,invoice_number', 'vmware:id,name,module_ip_address'])
->when($request->filter, fn($q, $v) => $q->where('server_name', 'like', "%{$v}%"))
->when($request->status, fn($q, $v) => $q->where('status', $v))
->when($request->customer_id, fn($q, $v) => $q->where('customer_id', $v));
$paginator = $query->latest()->paginate($perPage);
return response()->json([
'error' => false,
'data' => [
'items' => $paginator->items(),
'total' => $paginator->total(),
'per_page' => $paginator->perPage(),
'current_page' => $paginator->currentPage(),
'last_page' => $paginator->lastPage(),
],
]);
}
public function show(int $id): JsonResponse
{
$service = Service::with(['customer', 'product', 'orderItem', 'invoice', 'vmware', 'addons'])
->findOrFail($id);
return response()->json(['error' => false, 'data' => $service]);
}
public function detachCustomer(int $id): JsonResponse
{
$service = Service::findOrFail($id);
$service->update(['customer_id' => null]);
return response()->json(['error' => false, 'message' => 'Đã gỡ khách hàng khỏi dịch vụ.']);
}
}
@@ -1,63 +1,188 @@
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cập nhật mật khẩu — {{ $appName }}</title>
<style>
* { box-sizing: border-box; }
body { font-family: 'Segoe UI', Arial, sans-serif; background: #f0f4f8; margin: 0; padding: 0; color: #374151; }
.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: 32px 40px; text-align: center; }
.header .icon { font-size: 44px; margin-bottom: 8px; }
.header h1 { color: #fff; margin: 0; font-size: 22px; font-weight: 700; }
.body { padding: 32px 40px; line-height: 1.6; }
.credentials { background: #f9fafb; border: 1px dashed #d1d5db; border-radius: 8px; padding: 16px 18px; margin: 18px 0; font-size: 14px; }
.credentials .row { margin: 4px 0; }
.credentials .value { font-family: 'Menlo','Consolas',monospace; font-weight: 700; color: #111827; }
.footer { text-align: center; padding: 18px 40px; border-top: 1px solid #f3f4f6; font-size: 12px; color: #9ca3af; }
.cta-wrap { text-align: center; margin: 24px 0 4px; }
.cta-btn { display: inline-block; background-color: #16a34a; color: #ffffff !important; text-decoration: none; padding: 12px 26px; border-radius: 8px; font-size: 14px; font-weight: 600; }
</style>
</head>
<body>
<div class="wrapper">
<div class="card">
<div class="header">
<div class="icon">🔐</div>
<h1>Mật khẩu của bạn đã được cập nhật</h1>
</div>
@php
$appName = $appName ?? config('app.name', 'Thế Giới Số');
$fullName = $fullName ?? 'Quý khách';
$email = $email ?? '';
$password = $password ?? '';
$loginUrl = $loginUrl ?? rtrim((string) env('FRONTEND_URL', config('app.url')), '/') . '/dang-nhap';
$supportEmail = $supportEmail ?? 'support@tgs.com.vn';
$supportPhone = $supportPhone ?? '1900. 6119';
@endphp
<div class="body">
<p style="margin:0 0 12px 0;">
Xin chào <strong>{{ $fullName }}</strong>,
</p>
<p style="margin:0 0 12px 0;">
Quản trị viên của <strong>{{ $appName }}</strong> vừa cấp lại mật khẩu mới cho tài khoản của bạn.
Vui lòng sử dụng thông tin bên dưới để đăng nhập và đổi lại mật khẩu của bạn ngay sau đó.
</p>
<div class="credentials">
<div class="row">Tài khoản: <span class="value">{{ $email }}</span></div>
<div class="row">Mật khẩu mới: <span class="value">{{ $password }}</span></div>
</div>
<div class="cta-wrap">
<a href="{{ $loginUrl }}" class="cta-btn">Đăng nhập ngay</a>
</div>
<p style="margin:18px 0 0 0;color:#6b7280;">
Nếu bạn không yêu cầu thao tác này, vui lòng liên hệ ngay với chúng tôi qua
<strong>{{ $supportEmail }}</strong>.
</p>
</div>
<div class="footer">
Email này được gửi tự động. Vui lòng không trả lời email này.<br>
Cần hỗ trợ? Liên hệ <strong>{{ $supportEmail }}</strong> hoặc <strong>{{ $supportPhone }}</strong>
</div>
</div>
</div>
</body>
</html>
<table style="border-collapse: collapse; width: 100%; background-color: #efefef;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="padding: 20px;" align="center" valign="top">
<table style="padding-top: 20px; padding-bottom: 20px; border-collapse: collapse; background-color: #ffffff; width: 700px;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr class="NoiDungKM">
<td style="width: 700px;" valign="top">
<table style="border-collapse: collapse; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="background-color: #f06824; padding: 34px 30px 30px; text-align: center; font-family: Arial, Helvetica, sans-serif; color: #ffffff;" align="center" valign="top" bgcolor="#f06824">
<div style="font-size: 42px; line-height: 1; margin-bottom: 12px;">🔐</div>
<h1 style="margin: 0 0 8px; padding: 0; color: #ffffff; font-family: Arial, Helvetica, sans-serif; font-size: 24px; line-height: 1.35; font-weight: 700;">
Mật khẩu của bạn đã được cập nhật
</h1>
<div style="font-size: 14px; line-height: 1.6; color: #ffffff;">
Quản trị viên vừa cấp lại mật khẩu mới cho tài khoản của bạn
</div>
</td>
</tr>
<tr class="MXH">
<td style="text-align: center; padding: 0 30px;" align="center" valign="top">
<table style="border-collapse: collapse; background-color: #ffffff; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="height: 15px; font-family: Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.7; color: #000000;" valign="top" height="15">
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;">Xin chào <strong>{{ $fullName }}</strong>,</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;">
Quản trị viên của <strong>{{ $appName }}</strong> vừa cấp lại mật khẩu mới cho tài khoản của bạn.
Vui lòng sử dụng thông tin bên dưới để đăng nhập và đổi lại mật khẩu của bạn ngay sau đó.
</div>
<div style="text-align: left;">&nbsp;</div>
<table style="border-collapse: collapse; width: 100%; background-color: #f9fafb; border: 1px dashed #d1d5db; border-radius: 6px;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="padding: 14px 18px; font-family: Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.8; color: #000000;">
<div>Tài khoản: <strong style="font-family: 'Menlo','Consolas',monospace; color: #111827;">{{ $email }}</strong></div>
<div>Mật khẩu mới: <strong style="font-family: 'Menlo','Consolas',monospace; color: #111827;">{{ $password }}</strong></div>
</td>
</tr>
</tbody>
</table>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: center;">
<a style="display: inline-block; background-color: #f06824; color: #ffffff !important; text-decoration: none; padding: 12px 28px; border-radius: 3px; font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 700;" href="{{ $loginUrl }}" target="_blank" rel="noopener">
Đăng nhập ngay
</a>
</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left; color: #6b7280; font-size: 13px;">
Nếu bạn không yêu cầu thao tác này, vui lòng liên hệ ngay với chúng tôi qua <strong>{{ $supportEmail }}</strong>.
</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left; color: #666666; font-size: 13px;">
Email này được gửi tự động. Vui lòng không trả lời email này.
</div>
<div style="text-align: left;">&nbsp;</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr class="CacDV">
<td style="background-color: #f06824; width: 700px; color: #ffffff;" valign="top" bgcolor="#f06824">
<table style="border-collapse: collapse; background-color: #f06824; width: 100%; color: #ffffff;" border="0" cellspacing="0" cellpadding="0" align="left" bgcolor="#f06824">
<tbody>
<tr>
<td style="padding: 0;" width="100%">
<table style="border-collapse: collapse; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="width: 100%; height: 1px; background: #ffffff;" bgcolor="#ffffff" width="100%" height="1"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="height: 10px; color: #ffffff;" valign="top" height="10"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
<tr>
<td style="padding: 0; text-align: left; font-family: Helvetica, Arial, sans-serif; color: #ffffff; font-size: 16px; line-height: 150%;" align="center" valign="top">
<table style="border-collapse: collapse; color: #ffffff;" border="0" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://tgs.com.vn/premium-hosting/" target="_blank" rel="noopener"><span style="color: #ffffff;">Premium HOSTING</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://tgs.com.vn/cloud-server/" target="_blank" rel="noopener"><span style="color: #ffffff;">Cloud SERVER</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://cloudemail.vn/" target="_blank" rel="noopener"><span style="color: #ffffff;">EMAIL DOANH NGHIỆP</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://tgs.com.vn/thiet-ke-website/" target="_blank" rel="noopener"><span style="color: #ffffff;">Thiết Kế WEBSITE</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="height: 10px; color: #ffffff;" valign="top" height="10"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
<tr>
<td style="padding: 0;" width="100%">
<table style="border-collapse: collapse; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="width: 100%; height: 1px; background: #ffffff;" bgcolor="#ffffff" width="100%" height="1"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr class="ThongTin">
<td align="center" valign="top" bgcolor="#065d88">
<table style="border-collapse: collapse; background-color: #f06824; border-top: 0; border-bottom: 0; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 30px;">
<td style="height: 30px;" valign="top" height="10">&nbsp;</td>
</tr>
<tr style="height: 148px;">
<td style="padding: 0; font-family: Helvetica, Arial, sans-serif; text-decoration: none; color: #e7e7e7; font-size: 16px; font-weight: normal; line-height: 150%; vertical-align: top; text-align: center; height: 148px;" align="center">
<p style="margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; color: white; font-size: 11pt; line-height: 200%;">
<span style="color: #ffffff; font-family: 'times new roman', times, serif;"><strong style="font-size: 20px;">CÔNG TY CỔ PHẦN GIẢI PHÁP CÔNG NGHỆ THẾ GIỚI SỐ</strong></span><br>
<span style="color: #ffffff; font-family: arial, helvetica, sans-serif; font-size: 12pt;">Địa chỉ: 128/38 Tân Hương, Phường Phú Thọ Hòa, Thành Phố Hồ Chí Minh</span><br>
<span style="color: #ffffff; font-family: arial, helvetica, sans-serif; font-size: 12pt;">Tel: (028) 7309. 7379 - Tổng đài: {{ $supportPhone }}</span><br>
<span style="font-family: arial, helvetica, sans-serif; font-size: 12pt;">Email: <span style="color: #ffffff;"><a style="color: #ffffff; text-decoration: none;" href="mailto:{{ $supportEmail }}">{{ $supportEmail }}</a></span></span><br>
<span style="text-decoration: none; font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #ffffff !important;">
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.tgs.com.vn">www.TGS.com.vn</a> &amp;
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.thegioiso.cloud">www.thegioiso.CLOUD</a> &amp;
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.thegioiserver.com">www.thegioiSERVER.com</a> &amp;
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.cloudemail.vn">www.CloudEmail.vn</a>
</span>
</p>
<p style="margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; color: white; font-size: 11pt; line-height: 200%;">
<span style="text-decoration: none; font-family: 'times new roman', times, serif; color: #ffffff !important; font-size: 12pt;">
<span style="color: #ffffff; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt;">
<span style="text-decoration: none; font-family: 'times new roman', times, serif; color: #ffffff !important;">
<span style="font-family: arial, sans-serif;"><strong>🥇Thế Giới Số🥇</strong>- Premium HOSTING • CLOUD • SERVER &amp; WEBSITE | 15+ năm Phụng Sự 🚀</span>
</span>
</span>
</span>
</p>
</td>
</tr>
<tr>
<td style="height: 30px; color: #ffffff;" valign="top" height="30"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
+8
View File
@@ -19,6 +19,7 @@ use App\Http\Controllers\Api\Admin\Addon\AddonController;
use App\Http\Controllers\Api\Admin\WebDesign\WebDesignQuoteController;
use App\Http\Controllers\Api\Admin\Product\ProductCategoryController;
use App\Http\Controllers\Api\Admin\Product\ProductController;
use App\Http\Controllers\Api\Admin\Service\ServiceController;
use App\Http\Controllers\Api\Admin\VPS\VPSController;
use App\Http\Controllers\Api\Admin\VCenter\VCenterController;
use App\Http\Controllers\Api\Admin\DnsManager\ServerController as DnsServerController;
@@ -148,6 +149,13 @@ Route::prefix('v1')
Route::delete('{uuid}/force', 'forceDelete')->middleware('perm:customer.destroy');
});
// Services (module: customer)
Route::prefix('services')->controller(ServiceController::class)->group(function () {
Route::get('/', 'index')->middleware('perm:customer.view');
Route::get('{id}', 'show')->middleware('perm:customer.view');
Route::patch('{id}/detach-customer', 'detachCustomer')->middleware('perm:customer.edit');
});
// Products (module: product-service)
Route::prefix('products')
->controller(ProductController::class)