diff --git a/app/Enums/Client/ClientGender.php b/app/Enums/Client/ClientGender.php
new file mode 100644
index 0000000..2f57a62
--- /dev/null
+++ b/app/Enums/Client/ClientGender.php
@@ -0,0 +1,17 @@
+ 'Nam',
+ self::FEMALE => 'Nữ',
+ };
+ }
+}
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 8e7fbd1..bfb5955 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -34,8 +34,33 @@ class Handler extends ExceptionHandler
*/
public function register()
{
- $this->reportable(function (Throwable $e) {
- //
+ $this->renderable(function (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e, $request) {
+ if (!$request->expectsJson() && !$request->is('v1/*') && !$request->is('api/*')) {
+ return null;
+ }
+
+ $previous = $e->getPrevious();
+
+ if ($previous instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
+ $modelClass = $previous->getModel();
+ $pageType = match ($modelClass) {
+ \App\Models\Product::class => 'service',
+ \App\Models\Customer::class => 'client',
+ \App\Models\Post::class => 'post',
+ default => 'unknown',
+ };
+
+ return response()->json([
+ 'error' => true,
+ 'message' => 'Dữ liệu không tồn tại',
+ 'page_type' => $pageType,
+ ], 404);
+ }
+
+ return response()->json([
+ 'error' => true,
+ 'message' => 'API Route not found',
+ ], 404);
});
}
}
diff --git a/app/Http/Controllers/Api/Service/ServiceController.php b/app/Http/Controllers/Api/Service/ServiceController.php
new file mode 100644
index 0000000..1ac362d
--- /dev/null
+++ b/app/Http/Controllers/Api/Service/ServiceController.php
@@ -0,0 +1,45 @@
+serviceService = $serviceService;
+ }
+
+ /**
+ * Lấy danh sách dịch vụ (sản phẩm) đang hoạt động
+ */
+ public function getService()
+ {
+ $services = $this->serviceService->getActiveServices();
+
+ return response()->json([
+ 'error' => false,
+ 'message' => 'Lấy dữ liệu thành công',
+ 'page_type' => 'service',
+ 'data' => ServiceResource::collection($services),
+ ]);
+ }
+
+ public function getDetailService(Product $product)
+ {
+ $product->load(['osVersions.osGroup', 'category', 'pricings' => fn ($q) => $q->where('is_active', 1)]);
+
+ return response()->json([
+ 'error' => false,
+ 'message' => 'Lấy dữ liệu thành công',
+ 'page_type' => 'service',
+ 'data' => new ServiceResource($product),
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/admin/package/Datacenter.php b/app/Http/Controllers/admin/package/Datacenter.php
index df5f922..4613172 100644
--- a/app/Http/Controllers/admin/package/Datacenter.php
+++ b/app/Http/Controllers/admin/package/Datacenter.php
@@ -131,7 +131,7 @@ class Datacenter extends Controller
->addColumn(
'action',
function ($d) {
- return view('administrator.pages.package.datacenter.datatable.edit', ['row' => $d])->render();
+ return view('administrator.pages.package.datacenter.datatable.action', ['row' => $d])->render();
}
)
->filterColumn('v_center.host', function ($query, $keyword) {
diff --git a/app/Http/Resources/Api/Client/ClientResource.php b/app/Http/Resources/Api/Client/ClientResource.php
index 0191a62..809fc16 100644
--- a/app/Http/Resources/Api/Client/ClientResource.php
+++ b/app/Http/Resources/Api/Client/ClientResource.php
@@ -25,19 +25,15 @@ class ClientResource extends JsonResource
'phone' => $this->phone,
'address' => $this->address,
'city' => $this->city,
- 'ward' => $this->ward,
- 'province' => $this->province,
- 'active_phone' => 1,
- 'active_zalo' => 1,
- 'gender' => $this->gender,
+ 'ward' => $this->ward->name,
+ 'province' => $this->province->name,
+ 'gender' => $this->gender_label,
'date' => $this->created_at ? $this->created_at->format('Y-m-d H:i:s') : null,
'company' => $this->company ?? '',
- 'mst' => null,
'avatar' => $this->avatar,
- 'cmnd_after' => $this->cmnd_after,
- 'cmnd_before' => $this->cmnd_before,
- 'cmnd_active' => (bool) $this->identity_number,
- 'cmnd' => $this->cmnd,
+ 'cmnd_after' => $this->front_identity_number,
+ 'cmnd_before' => $this->back_identity_number,
+ 'cmnd' => $this->identity_number,
'fb_link' => null,
'active_2fa' => 0,
'off_mail_marketing' => $this->off_mail_marketing,
diff --git a/app/Http/Resources/Api/Service/ServiceResource.php b/app/Http/Resources/Api/Service/ServiceResource.php
new file mode 100644
index 0000000..12c8130
--- /dev/null
+++ b/app/Http/Resources/Api/Service/ServiceResource.php
@@ -0,0 +1,49 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ 'uuid' => $this->uuid,
+ 'name' => $this->name,
+ 'slug' => $this->slug,
+ 'short_description' => $this->short_description,
+ 'description' => $this->description,
+ 'thumbnail' => $this->thumbnail ? asset($this->thumbnail) : null,
+ 'product_type' => $this->product_type,
+ 'specs' => [
+ 'cpu' => $this->cpu,
+ 'ram' => $this->ram,
+ 'disk' => $this->disk,
+ 'bandwidth' => $this->bandwidth,
+ 'gpu' => $this->gpu
+ ],
+ 'stock' => $this->stock,
+ 'price' => $this->price,
+ 'price_sale' => $this->price_sale,
+ 'group' => $this->category->name,
+ 'os' => $this->osVersions->groupBy('os_group_id')->map(function ($versions) {
+ $group = $versions->first()->osGroup;
+ return [
+ 'name' => $group?->name,
+ 'thumbnail' => $group?->thumbnail,
+ 'versions' => $versions->map(fn($os) => [
+ 'version' => $os->version,
+ 'name' => $os->name,
+ ])->values(),
+ ];
+ })->values(),
+ ];
+ }
+}
diff --git a/app/Models/Customer.php b/app/Models/Customer.php
index 4cf9c34..1b77cd6 100644
--- a/app/Models/Customer.php
+++ b/app/Models/Customer.php
@@ -57,6 +57,11 @@ class Customer extends Authenticatable
'first_name',
'last_name',
'point',
+ 'gender_label',
+ ];
+
+ protected $casts = [
+ 'gender' => \App\Enums\Client\ClientGender::class,
];
/**
@@ -219,4 +224,9 @@ class Customer extends Authenticatable
{
return $this->belongsTo(Ward::class, 'ward_code', 'code');
}
+
+ public function getGenderLabelAttribute()
+ {
+ return $this->gender?->label() ?? 'Khác';
+ }
}
diff --git a/app/Providers/RepositoryProvider.php b/app/Providers/RepositoryProvider.php
index 938db95..c9abae5 100644
--- a/app/Providers/RepositoryProvider.php
+++ b/app/Providers/RepositoryProvider.php
@@ -365,6 +365,12 @@ class RepositoryProvider extends ServiceProvider
\App\Repositories\Api\Client\ClientRepositoryInterface::class,
\App\Repositories\Api\Client\ClientRepository::class
);
+
+ // Service API
+ $this->app->bind(
+ \App\Repositories\Api\Service\ServiceRepositoryInterface::class,
+ \App\Repositories\Api\Service\ServiceRepository::class
+ );
}
/**
diff --git a/app/Repositories/Api/Service/ServiceRepository.php b/app/Repositories/Api/Service/ServiceRepository.php
new file mode 100644
index 0000000..875dfed
--- /dev/null
+++ b/app/Repositories/Api/Service/ServiceRepository.php
@@ -0,0 +1,35 @@
+model
+ ->with(['osVersions.osGroup', 'category'])
+ ->where('is_active', 1)
+ ->orderBy('sort_order', 'asc')
+ ->get();
+ }
+
+ public function findActiveBySlug(string $slug)
+ {
+ return $this->model
+ ->with(['osVersions.osGroup', 'category', 'pricings' => fn ($q) => $q->where('is_active', 1)])
+ ->where('slug', $slug)
+ ->where('is_active', 1)
+ ->first();
+ }
+}
diff --git a/app/Repositories/Api/Service/ServiceRepositoryInterface.php b/app/Repositories/Api/Service/ServiceRepositoryInterface.php
new file mode 100644
index 0000000..e1d8740
--- /dev/null
+++ b/app/Repositories/Api/Service/ServiceRepositoryInterface.php
@@ -0,0 +1,15 @@
+serviceRepository = $serviceRepository;
+ }
+
+ /**
+ * Lấy danh sách dịch vụ đang hoạt động
+ */
+ public function getActiveServices()
+ {
+ return $this->serviceRepository->getActiveServices();
+ }
+
+ public function findActiveBySlug(string $slug)
+ {
+ return $this->serviceRepository->findActiveBySlug($slug);
+ }
+}
diff --git a/database/seeders/OsSeeder.php b/database/seeders/OsSeeder.php
new file mode 100644
index 0000000..2a144c0
--- /dev/null
+++ b/database/seeders/OsSeeder.php
@@ -0,0 +1,93 @@
+ 'Ubuntu',
+ 'thumbnail' => 'ubuntu.png',
+ 'module_host_id' => 1,
+ 'status' => 1,
+ 'versions' => [
+ '20.04 LTS',
+ '22.04 LTS',
+ '24.04 LTS',
+ ],
+ ],
+ [
+ 'name' => 'CentOS',
+ 'thumbnail' => 'centos.png',
+ 'module_host_id' => 1,
+ 'status' => 1,
+ 'versions' => [
+ '7',
+ '8 Stream',
+ ],
+ ],
+ [
+ 'name' => 'Debian',
+ 'thumbnail' => 'debian.png',
+ 'module_host_id' => 1,
+ 'status' => 1,
+ 'versions' => [
+ '10',
+ '11',
+ '12',
+ ],
+ ],
+ [
+ 'name' => 'Windows Server',
+ 'thumbnail' => 'windows.png',
+ 'module_host_id' => 1,
+ 'status' => 1,
+ 'versions' => [
+ '2016',
+ '2019',
+ '2022',
+ ],
+ ],
+ ];
+
+ foreach ($data as $groupData) {
+
+ /**
+ * Create or Update OS Group
+ */
+ $group = OsGroup::updateOrCreate(
+ ['name' => $groupData['name']],
+ [
+ 'thumbnail' => $groupData['thumbnail'],
+ 'module_host_id' => $groupData['module_host_id'],
+ 'status' => $groupData['status'],
+ ]
+ );
+
+ /**
+ * Create OS versions
+ */
+ foreach ($groupData['versions'] as $version) {
+ Os::updateOrCreate(
+ [
+ 'os_group_id' => $group->id,
+ 'version' => $version,
+ ],
+ [
+ 'template_id' => null,
+ 'status' => 1,
+ ]
+ );
+ }
+ }
+ }
+}
diff --git a/public/uploads/images/centos-svgrepo-com.svg b/public/uploads/images/centos.svg
similarity index 100%
rename from public/uploads/images/centos-svgrepo-com.svg
rename to public/uploads/images/centos.svg
diff --git a/public/uploads/images/ubuntu-svgrepo-com.svg b/public/uploads/images/icon-ubuntu.svg
similarity index 100%
rename from public/uploads/images/ubuntu-svgrepo-com.svg
rename to public/uploads/images/icon-ubuntu.svg
diff --git a/resources/images/os/icon-centos.svg b/resources/images/os/icon-centos.svg
new file mode 100644
index 0000000..c1897d8
--- /dev/null
+++ b/resources/images/os/icon-centos.svg
@@ -0,0 +1,14 @@
+
diff --git a/resources/images/os/icon-ubuntu.svg b/resources/images/os/icon-ubuntu.svg
new file mode 100644
index 0000000..02e8546
--- /dev/null
+++ b/resources/images/os/icon-ubuntu.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/resources/images/os/icon-windows.svg b/resources/images/os/icon-windows.svg
new file mode 100644
index 0000000..fb53a50
--- /dev/null
+++ b/resources/images/os/icon-windows.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/views/administrator/pages/block/Sync/All.blade.php b/resources/views/administrator/pages/block/Sync/All.blade.php
index a701a80..80301ce 100644
--- a/resources/views/administrator/pages/block/Sync/All.blade.php
+++ b/resources/views/administrator/pages/block/Sync/All.blade.php
@@ -1 +1,2 @@
-