Product management is working.
This commit is contained in:
parent
a1c7ee43f7
commit
17de29ec91
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Product;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Flux\Flux;
|
||||
|
||||
new class extends Component {
|
||||
#[Validate('required|string|max:50|unique:products,sku')]
|
||||
public string $sku = '';
|
||||
|
||||
#[Validate('required|string|max:255')]
|
||||
public string $name = '';
|
||||
|
||||
#[Validate('nullable|string|max:1000')]
|
||||
public string $description = '';
|
||||
|
||||
#[Validate('required|numeric|min:0')]
|
||||
public string $price = '';
|
||||
|
||||
#[Validate('boolean')]
|
||||
public bool $active = true;
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
Product::create([
|
||||
'sku' => $this->sku,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description ?: null,
|
||||
'price' => $this->price,
|
||||
'active' => $this->active,
|
||||
]);
|
||||
|
||||
$this->reset();
|
||||
Flux::modal('create-product')->close();
|
||||
$this->dispatch('product-created');
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<flux:modal.trigger name="create-product">
|
||||
<flux:button icon="plus" variant="primary">
|
||||
New Product
|
||||
</flux:button>
|
||||
</flux:modal.trigger>
|
||||
|
||||
<flux:modal name="create-product" class="md:w-96">
|
||||
<form wire:submit="save" class="space-y-6">
|
||||
<flux:heading size="lg">Create Product</flux:heading>
|
||||
|
||||
<flux:input label="SKU" wire:model="sku" />
|
||||
<flux:input label="Name" wire:model="name" />
|
||||
<flux:textarea label="Description" wire:model="description" rows="3" />
|
||||
<flux:input label="Price" wire:model="price" type="number" step="0.01" min="0" />
|
||||
<flux:switch label="Active" wire:model="active" />
|
||||
|
||||
<div class="flex gap-2">
|
||||
<flux:spacer />
|
||||
<flux:button type="submit" variant="primary">Create</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</flux:modal>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Product;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Attributes\On;
|
||||
use Flux\Flux;
|
||||
|
||||
new class extends Component {
|
||||
public ?int $productId = null;
|
||||
|
||||
#[Validate('required|string|max:50')]
|
||||
public string $sku = '';
|
||||
|
||||
#[Validate('required|string|max:255')]
|
||||
public string $name = '';
|
||||
|
||||
#[Validate('nullable|string|max:1000')]
|
||||
public string $description = '';
|
||||
|
||||
#[Validate('required|numeric|min:0')]
|
||||
public string $price = '';
|
||||
|
||||
#[Validate('boolean')]
|
||||
public bool $active = true;
|
||||
|
||||
#[On('edit-product')]
|
||||
public function edit(int $productId): void
|
||||
{
|
||||
$this->productId = $productId;
|
||||
$product = Product::findOrFail($productId);
|
||||
|
||||
$this->sku = $product->sku;
|
||||
$this->name = $product->name;
|
||||
$this->description = $product->description ?? '';
|
||||
$this->price = (string) $product->getRawOriginal('price') / 100;
|
||||
$this->active = $product->active;
|
||||
|
||||
$this->resetValidation();
|
||||
Flux::modal('edit-product')->show();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->validate([
|
||||
'sku' => 'required|string|max:50|unique:products,sku,' . $this->productId,
|
||||
'name' => 'required|string|max:255',
|
||||
'description' => 'nullable|string|max:1000',
|
||||
'price' => 'required|numeric|min:0',
|
||||
'active' => 'boolean',
|
||||
]);
|
||||
|
||||
$product = Product::findOrFail($this->productId);
|
||||
$product->update([
|
||||
'sku' => $this->sku,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description ?: null,
|
||||
'price' => $this->price,
|
||||
'active' => $this->active,
|
||||
]);
|
||||
|
||||
$this->reset();
|
||||
Flux::modal('edit-product')->close();
|
||||
$this->dispatch('product-updated');
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<flux:modal name="edit-product" class="md:w-96">
|
||||
<form wire:submit="save" class="space-y-6">
|
||||
<flux:heading size="lg">Edit Product</flux:heading>
|
||||
|
||||
<flux:input label="SKU" wire:model="sku" />
|
||||
<flux:input label="Name" wire:model="name" />
|
||||
<flux:textarea label="Description" wire:model="description" rows="3" />
|
||||
<flux:input label="Price" wire:model="price" type="number" step="0.01" min="0" />
|
||||
<flux:switch label="Active" wire:model="active" />
|
||||
|
||||
<div class="flex gap-2">
|
||||
<flux:spacer />
|
||||
<flux:button type="submit" variant="primary">Save</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</flux:modal>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Product;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
new class extends Component {
|
||||
use WithPagination;
|
||||
|
||||
public string $sortBy = 'name';
|
||||
public string $sortDirection = 'asc';
|
||||
|
||||
public function sort($column): void
|
||||
{
|
||||
if ($this->sortBy === $column) {
|
||||
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
$this->sortBy = $column;
|
||||
$this->sortDirection = 'asc';
|
||||
}
|
||||
}
|
||||
|
||||
#[On('product-created')]
|
||||
#[On('product-updated')]
|
||||
public function refresh(): void {}
|
||||
|
||||
#[Computed]
|
||||
public function products()
|
||||
{
|
||||
return Product::orderBy($this->sortBy, $this->sortDirection)->paginate(10);
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<!--suppress RequiredAttributes -->
|
||||
<div>
|
||||
<flux:table :paginate="$this->products">
|
||||
<flux:table.columns>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'sku'" :direction="$sortDirection"
|
||||
wire:click="sort('sku')">
|
||||
SKU
|
||||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'name'" :direction="$sortDirection"
|
||||
wire:click="sort('name')">
|
||||
Name
|
||||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'description'" :direction="$sortDirection"
|
||||
wire:click="sort('description')">
|
||||
Description
|
||||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'price'" :direction="$sortDirection"
|
||||
wire:click="sort('price')">
|
||||
Price
|
||||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'active'" :direction="$sortDirection"
|
||||
wire:click="sort('active')">
|
||||
Status
|
||||
</flux:table.column>
|
||||
<flux:table.column></flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@foreach($this->products as $product)
|
||||
<flux:table.row :key="$product->id">
|
||||
<flux:table.cell>{{ $product->sku }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $product->name }}</flux:table.cell>
|
||||
<flux:table.cell class="max-w-xs truncate">{{ $product->description }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $product->price }}</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<flux:badge :color="$product->active ? 'green' : 'zinc'">
|
||||
{{ $product->active ? 'Active' : 'Inactive' }}
|
||||
</flux:badge>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<flux:dropdown position="bottom" align="start">
|
||||
<flux:button variant="ghost" size="sm" icon="ellipsis-horizontal"
|
||||
inset="top bottom"></flux:button>
|
||||
|
||||
<flux:navmenu>
|
||||
<flux:menu.group heading="{{ $product->sku }}">
|
||||
<flux:menu.separator></flux:menu.separator>
|
||||
<flux:menu.item wire:click="$dispatch('edit-product', { productId: {{ $product->id }} })" icon="pencil">Edit</flux:menu.item>
|
||||
</flux:menu.group>
|
||||
</flux:navmenu>
|
||||
</flux:dropdown>
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforeach
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
</div>
|
||||
|
|
@ -20,10 +20,14 @@
|
|||
{{ __('Clients') }}
|
||||
</flux:sidebar.item>
|
||||
|
||||
<flux:sidebar.item icon="user" :href="route('contacts')" :current="request()->routeIs('clients.*')" wire:navigate>
|
||||
<flux:sidebar.item icon="user" :href="route('contacts')" :current="request()->routeIs('contacts.*')" wire:navigate>
|
||||
{{ __('Contacts') }}
|
||||
</flux:sidebar.item>
|
||||
|
||||
<flux:sidebar.item icon="archive-box" :href="route('products')" :current="request()->routeIs('products.*')" wire:navigate>
|
||||
{{ __('Products') }}
|
||||
</flux:sidebar.item>
|
||||
|
||||
</flux:sidebar.group>
|
||||
</flux:sidebar.nav>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<x-layouts::app :title="__('Products')">
|
||||
<div class="max-w-7xl mx-auto space-y-4">
|
||||
<div class="flex justify-end">
|
||||
<livewire:create-product />
|
||||
</div>
|
||||
<livewire:product-list />
|
||||
<livewire:edit-product />
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
|
@ -11,6 +11,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||
Route::view('dashboard', 'dashboard')->name('dashboard');
|
||||
Route::view('clients', 'clients.index')->name('clients');
|
||||
Route::view('contacts', 'contacts.index')->name('contacts');
|
||||
Route::view('products', 'products.index')->name('products');
|
||||
});
|
||||
|
||||
// Route::view('dashboard', 'dashboard')
|
||||
|
|
|
|||
Loading…
Reference in New Issue