feat: Implement VMware console access with a new API endpoint and UI, and update the admin header to display user details.
This commit is contained in:
@@ -186,27 +186,31 @@
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="profileDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<img class="w-30px h-30px ms-1 rounded-circle" src="{{ url('https://placehold.co/30x30') }}" alt="profile">
|
||||
<img class="w-30px h-30px ms-1 rounded-circle"
|
||||
src="https://ui-avatars.com/api/?name={{ auth()->user()->lastname }}+{{ auth()->user()->firstname }}&background=random"
|
||||
alt="profile">
|
||||
</a>
|
||||
<div class="dropdown-menu p-0" aria-labelledby="profileDropdown">
|
||||
<div class="d-flex flex-column align-items-center border-bottom px-5 py-3">
|
||||
<div class="mb-3">
|
||||
<img class="w-80px h-80px rounded-circle" src="{{ url('https://placehold.co/80x80') }}" alt="">
|
||||
<img class="w-80px h-80px rounded-circle"
|
||||
src="https://ui-avatars.com/api/?name={{ auth()->user()->lastname }}+{{ auth()->user()->firstname }}&background=random"
|
||||
alt="">
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="fs-16px fw-bolder">Amiah Burton</p>
|
||||
<p class="fs-12px text-secondary">amiahburton@gmail.com</p>
|
||||
<p class="fs-16px fw-bolder">{{ auth()->user()->lastname }} {{ auth()->user()->firstname }}</p>
|
||||
<p class="fs-12px text-secondary">{{ auth()->user()->email }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-unstyled p-1">
|
||||
<li>
|
||||
<a href="{{ url('/general/profile') }}" class="dropdown-item py-2 text-body ms-0">
|
||||
<a href="{{ route('account') }}" class="dropdown-item py-2 text-body ms-0">
|
||||
<i class="me-2 icon-md" data-lucide="user"></i>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:;" class="dropdown-item py-2 text-body ms-0">
|
||||
<a href="{{ route('account') }}" class="dropdown-item py-2 text-body ms-0">
|
||||
<i class="me-2 icon-md" data-lucide="edit"></i>
|
||||
<span>Edit Profile</span>
|
||||
</a>
|
||||
@@ -218,7 +222,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:;" class="dropdown-item py-2 text-body ms-0">
|
||||
<a href="{{ route('logout') }}" class="dropdown-item py-2 text-body ms-0">
|
||||
<i class="me-2 icon-md" data-lucide="log-out"></i>
|
||||
<span>Log Out</span>
|
||||
</a>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<div class="dt-action-dropdown dropdown">
|
||||
<button class="btn btn-sm btn-light border d-flex align-items-center gap-1" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<button class="btn btn-sm btn-light d-flex align-items-center gap-1" type="button" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<i data-lucide="more-vertical" width="14" height="14"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ route('products.edit', ['id' => $row->id]) }}">
|
||||
<i data-lucide="pencil" width="14" height="14"></i>
|
||||
<i data-lucide="pencil" width="14" height="14" class="me-2"></i>
|
||||
Edit
|
||||
</a>
|
||||
</li>
|
||||
@@ -13,22 +14,42 @@
|
||||
<form action="{{ route('products.duplicate', ['id' => $row->id]) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
<button type="submit" class="dropdown-item">
|
||||
<i data-lucide="copy" width="14" height="14"></i>
|
||||
<i data-lucide="copy" width="14" height="14" class="me-2"></i>
|
||||
Duplicate
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form action="{{ route('products.destroy', ['id' => $row->id]) }}" method="POST" class="d-inline"
|
||||
onsubmit="return confirm('Are you sure you want to delete this product?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="dropdown-item text-danger">
|
||||
<i data-lucide="trash-2" width="14" height="14"></i>
|
||||
<hr class="dropdown-divider">
|
||||
<li>
|
||||
<button type="button" class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<i data-lucide="trash-2" width="14" height="14" class="me-2 text-danger"></i>
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<form action="{{ route('products.destroy', ['id' => $row->id]) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<input type="hidden" name="id" value="{{ $row->id }}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Xoá sản phẩm</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Bạn có chắc chắn muốn xoá sản phẩm này?
|
||||
<strong>{{ $row->name }}</strong>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Huỷ</button>
|
||||
<button type="submit" class="btn btn-danger" id="confirmDeleteBtn">Xoá</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,231 +16,227 @@ $breadcrumbItems = [
|
||||
<x-admin-breadcrumb :items="$breadcrumbItems" />
|
||||
@endsection
|
||||
@section('content')
|
||||
<form action="{{ route('products.update', $product->id) }}" method="post">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="row align-items-start">
|
||||
{{-- Left Column: Basic Info & Pricing --}}
|
||||
<div class="col-md-8">
|
||||
<div class="card card-primary card-outline">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title m-0">Edit Product</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('products.update', $product->id) }}" method="post">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="row align-items-start">
|
||||
<div class="col-md-8">
|
||||
<div class="card card-primary card-outline">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="card-title m-0">Edit Product</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<h5 class="text-primary border-bottom mb-3 pb-2">Basic Information</h5>
|
||||
|
||||
{{-- Basic Information --}}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-primary border-bottom mb-3 pb-2">Basic Information</h5>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label for="name">Product Name <span class="text-danger">*</span></label>
|
||||
<input type="text" id="name" name="name" value="{{ old('name', $product->name) }}"
|
||||
class="form-control" required placeholder="Enter product name" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label for="name">Product Name <span class="text-danger">*</span></label>
|
||||
<input type="text" id="name" name="name"
|
||||
value="{{ old('name', $product->name) }}"
|
||||
class="form-control" required placeholder="Enter product name" />
|
||||
<div class="form-group mb-3">
|
||||
<label for="short_description">Short Description</label>
|
||||
<textarea name="short_description" id="short_description" rows="2" class="form-control"
|
||||
placeholder="Brief summary">{{ old('short_description', $product->short_description) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="description">Full Description</label>
|
||||
<textarea name="description" id="description" rows="4" class="form-control"
|
||||
placeholder="Detailed description">{{ old('description', $product->description) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="short_description">Short Description</label>
|
||||
<textarea name="short_description" id="short_description" rows="2" class="form-control"
|
||||
placeholder="Brief summary">{{ old('short_description', $product->short_description) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="description">Full Description</label>
|
||||
<textarea name="description" id="description" rows="4" class="form-control"
|
||||
placeholder="Detailed description">{{ old('description', $product->description) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Pricing & Billing --}}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-primary border-bottom mb-3 pb-2">Pricing & Billing</h5>
|
||||
{{-- Global Settings --}}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="currency">Currency</label>
|
||||
<select name="currency" id="currency" class="form-control select2">
|
||||
<option value="USD"
|
||||
{{ old('currency', $product->currency ?? 'USD') == 'USD' ? 'selected' : '' }}>
|
||||
USD</option>
|
||||
<option value="VND"
|
||||
{{ old('currency', $product->currency ?? 'VND') == 'VND' ? 'selected' : '' }}>
|
||||
VND</option>
|
||||
</select>
|
||||
{{-- Pricing & Billing --}}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-primary border-bottom mb-3 pb-2">Pricing & Billing</h5>
|
||||
{{-- Global Settings --}}
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="currency">Currency</label>
|
||||
<select name="currency" id="currency" class="form-control select2">
|
||||
<option value="USD"
|
||||
{{ old('currency', $product->currency ?? 'USD') == 'USD' ? 'selected' : '' }}>
|
||||
USD</option>
|
||||
<option value="VND"
|
||||
{{ old('currency', $product->currency ?? 'VND') == 'VND' ? 'selected' : '' }}>
|
||||
VND</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="setup_fee">Setup Fee</label>
|
||||
<input type="number" step="0.01" id="setup_fee" name="setup_fee"
|
||||
value="{{ old('setup_fee', $product->setup_fee ?? 0) }}" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="trial_days">Trial Days</label>
|
||||
<input type="number" id="trial_days" name="trial_days"
|
||||
value="{{ old('trial_days', $product->trial_days ?? 0) }}" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="setup_fee">Setup Fee</label>
|
||||
<input type="number" step="0.01" id="setup_fee" name="setup_fee"
|
||||
value="{{ old('setup_fee', $product->setup_fee ?? 0) }}" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="trial_days">Trial Days</label>
|
||||
<input type="number" id="trial_days" name="trial_days"
|
||||
value="{{ old('trial_days', $product->trial_days ?? 0) }}" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{-- Billing Cycles Table --}}
|
||||
<label class="fw-bold mb-2">Billing Cycles</label>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 50px;">#</th>
|
||||
<th scope="col">Billing Cycle</th>
|
||||
<th scope="col">Price</th>
|
||||
<th scope="col">Sale Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (\App\Enums\Product\ProductBillingCycle::cases() as $cycle)
|
||||
@php
|
||||
{{-- Billing Cycles Table --}}
|
||||
<label class="fw-bold mb-2">Billing Cycles</label>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 50px;">#</th>
|
||||
<th scope="col">Billing Cycle</th>
|
||||
<th scope="col">Price</th>
|
||||
<th scope="col">Sale Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (\App\Enums\Product\ProductBillingCycle::cases() as $cycle)
|
||||
@php
|
||||
$pricing = $product->pricings
|
||||
->where('billing_cycle', $cycle->value)
|
||||
->first();
|
||||
@endphp
|
||||
<tr>
|
||||
<th scope="row" class="text-center align-middle">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
id="cycle_{{ $cycle->value }}"
|
||||
name="pricing[{{ $cycle->value }}][enabled]" value="1"
|
||||
{{ old("pricing.{$cycle->value}.enabled", $pricing ? 1 : 0) ? 'checked' : '' }}>
|
||||
<label class="form-check-label"
|
||||
for="cycle_{{ $cycle->value }}"></label>
|
||||
</div>
|
||||
</th>
|
||||
<td class="align-middle">
|
||||
{{ $cycle->label() }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" step="0.01" class="form-control"
|
||||
name="pricing[{{ $cycle->value }}][price]" placeholder="0.00"
|
||||
value="{{ old("pricing.{$cycle->value}.price", $pricing->price ?? '') }}">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" step="0.01" class="form-control"
|
||||
name="pricing[{{ $cycle->value }}][price_sale]"
|
||||
placeholder="0.00"
|
||||
value="{{ old("pricing.{$cycle->value}.price_sale", $pricing->price_sale ?? '') }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endphp
|
||||
<tr>
|
||||
<th scope="row" class="text-center align-middle">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
id="cycle_{{ $cycle->value }}"
|
||||
name="pricing[{{ $cycle->value }}][enabled]" value="1"
|
||||
{{ old("pricing.{$cycle->value}.enabled", $pricing ? 1 : 0) ? 'checked' : '' }}>
|
||||
<label class="form-check-label"
|
||||
for="cycle_{{ $cycle->value }}"></label>
|
||||
</div>
|
||||
</th>
|
||||
<td class="align-middle">
|
||||
{{ $cycle->label() }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" step="0.01" class="form-control"
|
||||
name="pricing[{{ $cycle->value }}][price]" placeholder="0.00"
|
||||
value="{{ old("pricing.{$cycle->value}.price", $pricing->price ?? '') }}">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" step="0.01" class="form-control"
|
||||
name="pricing[{{ $cycle->value }}][price_sale]"
|
||||
placeholder="0.00"
|
||||
value="{{ old("pricing.{$cycle->value}.price_sale", $pricing->price_sale ?? '') }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Hardware Specs (VPS/Dedicated) --}}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-info border-bottom mb-3 pb-2">Hardware Specifications</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="cpu">CPU</label>
|
||||
<input type="text" name="cpu" id="cpu" class="form-control"
|
||||
value="{{ old('cpu', $product->cpu) }}" placeholder="e.g. 2 Core" />
|
||||
{{-- Hardware Specs (VPS/Dedicated) --}}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-info border-bottom mb-3 pb-2">Hardware Specifications</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="cpu">CPU</label>
|
||||
<input type="text" name="cpu" id="cpu" class="form-control"
|
||||
value="{{ old('cpu', $product->cpu) }}" placeholder="e.g. 2 Core" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="ram">RAM</label>
|
||||
<input type="text" name="ram" id="ram" class="form-control"
|
||||
value="{{ old('ram', $product->ram) }}" placeholder="e.g. 4 GB" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="disk">Disk/Storage</label>
|
||||
<input type="text" name="disk" id="disk" class="form-control"
|
||||
value="{{ old('disk', $product->disk) }}" placeholder="e.g. 50 GB NVMe" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="ram">RAM</label>
|
||||
<input type="text" name="ram" id="ram" class="form-control"
|
||||
value="{{ old('ram', $product->ram) }}" placeholder="e.g. 4 GB" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="disk">Disk/Storage</label>
|
||||
<input type="text" name="disk" id="disk" class="form-control"
|
||||
value="{{ old('disk', $product->disk) }}" placeholder="e.g. 50 GB NVMe" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="bandwidth">Bandwidth</label>
|
||||
<input type="text" name="bandwidth" id="bandwidth" class="form-control"
|
||||
value="{{ old('bandwidth', $product->bandwidth) }}" placeholder="e.g. Unlimited" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="gpu">GPU</label>
|
||||
<input type="text" name="gpu" id="gpu" class="form-control"
|
||||
value="{{ old('gpu', $product->gpu) }}" placeholder="Optional">
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="bandwidth">Bandwidth</label>
|
||||
<input type="text" name="bandwidth" id="bandwidth" class="form-control"
|
||||
value="{{ old('bandwidth', $product->bandwidth) }}" placeholder="e.g. Unlimited" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="gpu">GPU</label>
|
||||
<input type="text" name="gpu" id="gpu" class="form-control"
|
||||
value="{{ old('gpu', $product->gpu) }}" placeholder="Optional">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Right Column: Classification & Settings --}}
|
||||
<div class="col-md-4 sticky-top top-10 z-0">
|
||||
<div class="card card-secondary card-outline mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title m-0">Classification</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{-- Product Category --}}
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label for="product_categories_id">Group</label>
|
||||
<a href="{{ route('products.group.index') }}"
|
||||
class="text-primary text-decoration-underline">
|
||||
<i data-lucide="plus" class="w-12px h-12px"></i>
|
||||
Thêm Group
|
||||
</a>
|
||||
{{-- Right Column: Classification & Settings --}}
|
||||
<div class="col-md-4 sticky-top top-10 z-0">
|
||||
<div class="card card-secondary card-outline mb-3">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="card-title m-0">Classification</h5>
|
||||
</div>
|
||||
@include('administrator.pages.product.categories.partials.category-tree', [
|
||||
'categories' => $group,
|
||||
'selected' => old('product_categories_id', $product->product_categories_id),
|
||||
'name' => 'product_categories_id',
|
||||
])
|
||||
@error('product_categories_id')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{-- Product Category --}}
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label for="product_categories_id">Group</label>
|
||||
<a href="{{ route('products.group.index') }}"
|
||||
class="text-primary text-decoration-underline">
|
||||
<i data-lucide="plus" class="w-12px h-12px"></i>
|
||||
Thêm Group
|
||||
</a>
|
||||
</div>
|
||||
@include('administrator.pages.product.categories.partials.category-tree', [
|
||||
'categories' => $group,
|
||||
'selected' => old('product_categories_id', $product->product_categories_id),
|
||||
'name' => 'product_categories_id',
|
||||
])
|
||||
@error('product_categories_id')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Operating System (for VPS) --}}
|
||||
<div class="form-group mb-3" id="os-section">
|
||||
<label for="os_ids" class="mb-2">Operating System</label>
|
||||
@php
|
||||
{{-- Operating System (for VPS) --}}
|
||||
<div class="form-group mb-3" id="os-section">
|
||||
<label for="os_ids" class="mb-2">Operating System</label>
|
||||
@php
|
||||
$selectedOsIds = old('os_ids', $product->osVersions->pluck('id')->toArray());
|
||||
@endphp
|
||||
<select class="form-control select2" name="os_ids[]" id="os_ids" multiple
|
||||
data-placeholder="-- Select OS versions --">
|
||||
@foreach ($osVersions as $osVersion)
|
||||
<option value="{{ $osVersion->id }}"
|
||||
{{ in_array($osVersion->id, $selectedOsIds) ? 'selected' : '' }}>
|
||||
{{ $osVersion->osGroup->name ?? 'Unknown' }} - {{ $osVersion->version }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Select multiple OS versions supported by this product</small>
|
||||
</div>
|
||||
@endphp
|
||||
<select class="form-control select2" name="os_ids[]" id="os_ids" multiple
|
||||
data-placeholder="-- Select OS versions --">
|
||||
@foreach ($osVersions as $osVersion)
|
||||
<option value="{{ $osVersion->id }}"
|
||||
{{ in_array($osVersion->id, $selectedOsIds) ? 'selected' : '' }}>
|
||||
{{ $osVersion->osGroup->name ?? 'Unknown' }} - {{ $osVersion->version }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Select multiple OS versions supported by this product</small>
|
||||
</div>
|
||||
|
||||
{{-- Control Panels (for Hosting) --}}
|
||||
<div class="form-group mb-3" id="control-panel-section" style="display: none;">
|
||||
<label for="control_panel_ids" class="mb-2">Control Panels</label>
|
||||
@php
|
||||
{{-- Control Panels (for Hosting) --}}
|
||||
<div class="form-group mb-3" id="control-panel-section" style="display: none;">
|
||||
<label for="control_panel_ids" class="mb-2">Control Panels</label>
|
||||
@php
|
||||
$selectedPanelIds = old('control_panel_ids', $product->controlPanels->pluck('id')->toArray());
|
||||
$panelPriceModifiers = old(
|
||||
'control_panel_price_modifiers',
|
||||
@@ -250,117 +246,117 @@ $breadcrumbItems = [
|
||||
'control_panel_default',
|
||||
$product->controlPanels->where('pivot.is_default', true)->first()?->id
|
||||
);
|
||||
@endphp
|
||||
<select class="form-control select2" name="control_panel_ids[]" id="control_panel_ids" multiple data-placeholder="-- Select Control Panels --">
|
||||
@foreach ($controlPanels as $panel)
|
||||
<option value="{{ $panel->id }}" {{ in_array($panel->id, $selectedPanelIds) ? 'selected' : '' }}>
|
||||
{{ $panel->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Select control panels available for this hosting product</small>
|
||||
@endphp
|
||||
<select class="form-control select2" name="control_panel_ids[]" id="control_panel_ids" multiple data-placeholder="-- Select Control Panels --">
|
||||
@foreach ($controlPanels as $panel)
|
||||
<option value="{{ $panel->id }}" {{ in_array($panel->id, $selectedPanelIds) ? 'selected' : '' }}>
|
||||
{{ $panel->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Select control panels available for this hosting product</small>
|
||||
|
||||
{{-- Price modifiers for each panel --}}
|
||||
<div id="panel-price-modifiers" class="mt-3">
|
||||
@foreach ($controlPanels as $panel)
|
||||
<div class="panel-price-row mb-2" id="panel-price-{{ $panel->id }}"
|
||||
style="display: {{ in_array($panel->id, $selectedPanelIds) ? 'block' : 'none' }};">
|
||||
<label class="form-label">{{ $panel->name }} - Phụ phí (VNĐ)</label>
|
||||
<div class="input-group">
|
||||
<input type="number"
|
||||
class="form-control"
|
||||
name="control_panel_price_modifiers[{{ $panel->id }}]"
|
||||
placeholder="0"
|
||||
min="0"
|
||||
step="1000"
|
||||
value="{{ $panelPriceModifiers[$panel->id] ?? 0 }}">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="radio"
|
||||
name="control_panel_default"
|
||||
value="{{ $panel->id }}"
|
||||
{{ $defaultPanel == $panel->id ? 'checked' : '' }}>
|
||||
<span class="ms-1">Mặc định</span>
|
||||
{{-- Price modifiers for each panel --}}
|
||||
<div id="panel-price-modifiers" class="mt-3">
|
||||
@foreach ($controlPanels as $panel)
|
||||
<div class="panel-price-row mb-2" id="panel-price-{{ $panel->id }}"
|
||||
style="display: {{ in_array($panel->id, $selectedPanelIds) ? 'block' : 'none' }};">
|
||||
<label class="form-label">{{ $panel->name }} - Phụ phí (VNĐ)</label>
|
||||
<div class="input-group">
|
||||
<input type="number"
|
||||
class="form-control"
|
||||
name="control_panel_price_modifiers[{{ $panel->id }}]"
|
||||
placeholder="0"
|
||||
min="0"
|
||||
step="1000"
|
||||
value="{{ $panelPriceModifiers[$panel->id] ?? 0 }}">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<input type="radio"
|
||||
name="control_panel_default"
|
||||
value="{{ $panel->id }}"
|
||||
{{ $defaultPanel == $panel->id ? 'checked' : '' }}>
|
||||
<span class="ms-1">Mặc định</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- Product Type --}}
|
||||
<div class="form-group mb-3">
|
||||
<label for="product_type" class="mb-2">Product Type</label>
|
||||
<select class="form-control select2" name="product_type" id="product_type">
|
||||
<option value="">-- Select Type --</option>
|
||||
@foreach (\App\Enums\Product\ProductTypeEnums::cases() as $type)
|
||||
<option value="{{ $type->value }}"
|
||||
{{ old('product_type', $product->product_type) == $type->value ? 'selected' : '' }}>
|
||||
{{ $type->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- Status --}}
|
||||
<div class="form-group mb-3">
|
||||
<label for="is_active" class="mb-2">Status</label>
|
||||
<select class="form-control select2" name="is_active" id="is_active">
|
||||
@foreach (\App\Enums\Status::cases() as $status)
|
||||
<option value="{{ $status->value }}"
|
||||
{{ old('is_active', $product->is_active ? 1 : 0) == $status->value ? 'selected' : '' }}>
|
||||
{{ $status->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="sort_order" class="mb-2">Sort Order</label>
|
||||
<input type="number" name="sort_order" id="sort_order" class="form-control"
|
||||
value="{{ old('sort_order', $product->sort_order ?? 0) }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Product Type --}}
|
||||
<div class="form-group mb-3">
|
||||
<label for="product_type" class="mb-2">Product Type</label>
|
||||
<select class="form-control select2" name="product_type" id="product_type">
|
||||
<option value="">-- Select Type --</option>
|
||||
@foreach (\App\Enums\Product\ProductTypeEnums::cases() as $type)
|
||||
<option value="{{ $type->value }}"
|
||||
{{ old('product_type', $product->product_type) == $type->value ? 'selected' : '' }}>
|
||||
{{ $type->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="card card-secondary card-outline">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="card-title m-0">Inventory & Limits</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="stock">Stock Quantity</label>
|
||||
<input type="number" name="stock" id="stock" class="form-control"
|
||||
placeholder="Unlimited if empty" value="{{ old('stock', $product->stock) }}">
|
||||
</div>
|
||||
|
||||
{{-- Status --}}
|
||||
<div class="form-group mb-3">
|
||||
<label for="is_active" class="mb-2">Status</label>
|
||||
<select class="form-control select2" name="is_active" id="is_active">
|
||||
@foreach (\App\Enums\Status::cases() as $status)
|
||||
<option value="{{ $status->value }}"
|
||||
{{ old('is_active', $product->is_active ? 1 : 0) == $status->value ? 'selected' : '' }}>
|
||||
{{ $status->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="is_unlimited"
|
||||
name="is_unlimited" value="1"
|
||||
{{ old('is_unlimited', $product->is_unlimited) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="is_unlimited">Unlimited Stock</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="sort_order" class="mb-2">Sort Order</label>
|
||||
<input type="number" name="sort_order" id="sort_order" class="form-control"
|
||||
value="{{ old('sort_order', $product->sort_order ?? 0) }}">
|
||||
<div class="form-group mt-3">
|
||||
<label for="published_at">Publish Date</label>
|
||||
<input type="date" name="published_at" id="published_at" class="form-control"
|
||||
value="{{ old('published_at', $product->published_at?->format('Y-m-d')) }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-secondary card-outline">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title m-0">Inventory & Limits</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="stock">Stock Quantity</label>
|
||||
<input type="number" name="stock" id="stock" class="form-control"
|
||||
placeholder="Unlimited if empty" value="{{ old('stock', $product->stock) }}">
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="is_unlimited"
|
||||
name="is_unlimited" value="1"
|
||||
{{ old('is_unlimited', $product->is_unlimited) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="is_unlimited">Unlimited Stock</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<label for="published_at">Publish Date</label>
|
||||
<input type="date" name="published_at" id="published_at" class="form-control"
|
||||
value="{{ old('published_at', $product->published_at?->format('Y-m-d')) }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-fixed-action-bar>
|
||||
<x-button-crm type="button" variant="secondary" size="sm" icon="arrow-left"
|
||||
route="products.index">Cancel</x-button-crm>
|
||||
<x-button-crm type="submit" variant="primary" size="sm" icon="save">Update
|
||||
Product</x-button-crm>
|
||||
</x-fixed-action-bar>
|
||||
</form>
|
||||
<x-fixed-action-bar>
|
||||
<x-button-crm type="button" variant="secondary" size="sm" icon="arrow-left"
|
||||
route="products.index">Cancel</x-button-crm>
|
||||
<x-button-crm type="submit" variant="primary" size="sm" icon="save">Update
|
||||
Product</x-button-crm>
|
||||
</x-fixed-action-bar>
|
||||
</form>
|
||||
@endsection
|
||||
@push('plugin-scripts')
|
||||
@foreach (config('assets.datatable.js') as $datatable)
|
||||
|
||||
@@ -7,47 +7,46 @@
|
||||
|
||||
@section('breadcrumb')
|
||||
@php
|
||||
$breadcrumbItems = [
|
||||
['label' => 'Product'],
|
||||
['label' => 'Product / Services'],
|
||||
['label' => 'Product List', 'active' => true],
|
||||
];
|
||||
$breadcrumbItems = [
|
||||
['label' => 'Product'],
|
||||
['label' => 'Product / Services'],
|
||||
['label' => 'Product List', 'active' => true],
|
||||
];
|
||||
@endphp
|
||||
<x-admin-breadcrumb :items="$breadcrumbItems" />
|
||||
@endsection
|
||||
@section('tab')
|
||||
@php
|
||||
$tabs = [
|
||||
[
|
||||
'label' => 'Product',
|
||||
'route' => 'products.index',
|
||||
'icon' => 'server',
|
||||
],
|
||||
[
|
||||
'label' => 'Product Group',
|
||||
'route' => 'products.group.index',
|
||||
'icon' => 'server',
|
||||
],
|
||||
];
|
||||
$tabs = [
|
||||
[
|
||||
'label' => 'Product',
|
||||
'route' => 'products.index',
|
||||
'icon' => 'server',
|
||||
],
|
||||
[
|
||||
'label' => 'Product Group',
|
||||
'route' => 'products.group.index',
|
||||
'icon' => 'server',
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
<x-admin-tab :items="$tabs" />
|
||||
@endsection
|
||||
@section('content')
|
||||
@if (auth()->guard('admins')->user()->can('manager product'))
|
||||
<div class="card card-primary card-outline rounded-4 shadow-none">
|
||||
<div class="card-header d-flex justify-content-end gap-2 rounded-top-4">
|
||||
<div class="card-header bg-white d-flex justify-content-end gap-2 rounded-top-4">
|
||||
@include('administrator.pages.block.product.btn')
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@php
|
||||
$productColumns = [
|
||||
['key' => 'checkbox', 'label' => '<input type="checkbox" class="form-check-input" id="dt-master-checkbox">', 'orderable' => false, 'searchable' => false, 'className' => 'text-center'],
|
||||
['key' => 'name', 'label' => 'Product Name'],
|
||||
['key' => 'category', 'label' => 'Category / Type', 'searchable' => false],
|
||||
['key' => 'price', 'label' => 'Price', 'orderable' => false, 'searchable' => false],
|
||||
['key' => 'specs', 'label' => 'Specifications', 'orderable' => false, 'searchable' => false],
|
||||
['key' => 'status', 'label' => 'Status', 'searchable' => false],
|
||||
['key' => 'action', 'label' => '', 'orderable' => false, 'searchable' => false, 'className' => 'text-center'],
|
||||
['key' => 'category', 'label' => 'Category / Type'],
|
||||
['key' => 'price', 'label' => 'Price'],
|
||||
['key' => 'specs', 'label' => 'Specifications'],
|
||||
['key' => 'status', 'label' => 'Status'],
|
||||
['key' => 'action', 'label' => 'Action'],
|
||||
];
|
||||
@endphp
|
||||
<x-datatable id="productTable" ajax="{{ route('products.datatable') }}" :columns="$productColumns" />
|
||||
@@ -64,4 +63,4 @@ $tabs = [
|
||||
@foreach (config('assets.datatable.js') as $datatable)
|
||||
<script src="{{ asset($datatable) }}"></script>
|
||||
@endforeach
|
||||
@endpush
|
||||
@endpush
|
||||
@@ -346,73 +346,6 @@ $dtColumns = collect($columns)
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.data-table-wrapper .table thead th {
|
||||
border-color: #f1f5f9 !important;
|
||||
font-family: 'Inter', sans-serif;
|
||||
vertical-align: middle;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background-color: #f8f9fa;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.table thead th:last-child,
|
||||
.table tbody td:last-child {
|
||||
position: sticky;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
z-index: 1;
|
||||
border-left: 1px solid #f1f5f9;
|
||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.02);
|
||||
width: 70px;
|
||||
min-width: 70px;
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
.table thead th:last-child {
|
||||
background: #f8f9fa;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.table thead th:first-child,
|
||||
.table tbody td:first-child {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
z-index: 1;
|
||||
border-right: 1px solid #f1f5f9;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.table thead th:first-child {
|
||||
background: #f8f9fa;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.table tbody tr:hover td {
|
||||
background-color: #f8fafc !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.data-table-wrapper .table td:not(:last-child) {
|
||||
border-right: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.data-table-wrapper .table thead tr.filter-row th {
|
||||
top: 45px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.data-table-wrapper .input-group-text {
|
||||
color: #94a3b8;
|
||||
}
|
||||
@@ -584,35 +517,5 @@ $dtColumns = collect($columns)
|
||||
gap: 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dt-action-dropdown .dropdown-menu {
|
||||
min-width: 140px;
|
||||
padding: 0.25rem 0;
|
||||
font-size: 0.8125rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e2e8f0;
|
||||
z-index: 1050;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.dt-action-dropdown .dropdown-item {
|
||||
padding: 0.4rem 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.dt-action-dropdown .dropdown-item:hover {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
.dt-action-dropdown .dropdown-item.text-danger:hover {
|
||||
background-color: #fef2f2;
|
||||
}
|
||||
|
||||
.dt-action-dropdown .dropdown-divider {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user