feat(warehouse): membuat fitur gudang
This commit is contained in:
parent
a2efc0f349
commit
77babcf44c
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Datatable\Manage;
|
||||
namespace App\Livewire\Datatable\Master;
|
||||
|
||||
use App\Models\Warehouse;
|
||||
use App\Traits\Datatable\WithAppendColumn;
|
||||
@ -25,6 +25,12 @@ public function columns(): array
|
||||
->label(function ($row) {
|
||||
$actions = '';
|
||||
|
||||
if (auth()->user()->can('show warehouse')) {
|
||||
$actions .= view('components.actions.table.show', [
|
||||
'showRoute' => route('studio.master.warehouse.show', $row->hash),
|
||||
])->render();
|
||||
}
|
||||
|
||||
if (auth()->user()->can('update warehouse')) {
|
||||
$actions .= view('components.actions.table.edit-modal', [
|
||||
'id' => $row->hash,
|
||||
@ -42,7 +48,7 @@ public function columns(): array
|
||||
return $actions;
|
||||
})
|
||||
->html()
|
||||
->hideIf(auth()->user()->cannot('update warehouse') && auth()->user()->cannot('delete warehouse')),
|
||||
->hideIf(auth()->user()->cannot('view warehouse') && auth()->user()->cannot('update warehouse') && auth()->user()->cannot('delete warehouse')),
|
||||
];
|
||||
}
|
||||
|
||||
89
app/Livewire/Studio/Master/Warehouse/Index.php
Normal file
89
app/Livewire/Studio/Master/Warehouse/Index.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Master\Warehouse;
|
||||
|
||||
use App\Livewire\Forms\Studio\Master\WarehouseForm;
|
||||
use App\Models\Warehouse as WarehouseModel;
|
||||
use App\Traits\Authorization\WithAuthorization;
|
||||
use App\Traits\Components\WithCloseModal;
|
||||
use App\Traits\Components\WithConfirmation;
|
||||
use App\Traits\Components\WithToast;
|
||||
use App\Traits\Notification\WithSubscribeNotification;
|
||||
use App\Traits\Utilities\WithUpdatedData;
|
||||
use Flux\Flux;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Gudang')]
|
||||
class Index extends Component
|
||||
{
|
||||
use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData;
|
||||
|
||||
public WarehouseForm $form;
|
||||
|
||||
public string $method = 'create';
|
||||
|
||||
public string $modalTitle = '';
|
||||
|
||||
public function create(): void
|
||||
{
|
||||
$this->canOrAbort('create warehouse');
|
||||
|
||||
$this->form->store();
|
||||
|
||||
$this->dispatch('refreshDatatable');
|
||||
|
||||
$this->toast('Gudang berhasil ditambahkan.');
|
||||
|
||||
Flux::modals()->close();
|
||||
}
|
||||
|
||||
public function update(): void
|
||||
{
|
||||
$this->canOrAbort('update warehouse');
|
||||
|
||||
$this->form->update();
|
||||
|
||||
$this->dispatch('refreshDatatable');
|
||||
|
||||
$this->toast('Gudang berhasil diperbarui.');
|
||||
|
||||
Flux::modals()->close();
|
||||
}
|
||||
|
||||
public function delete(WarehouseModel $warehouse): void
|
||||
{
|
||||
$this->canOrAbort('delete warehouse');
|
||||
|
||||
$this->form->warehouse = $warehouse;
|
||||
|
||||
$this->form->delete();
|
||||
|
||||
$this->dispatch('refreshDatatable');
|
||||
|
||||
$this->toast('Gudang berhasil dihapus.');
|
||||
|
||||
Flux::modals()->close();
|
||||
}
|
||||
|
||||
#[On('modal:open')]
|
||||
public function openModal(string $method, string $modalTitle, ?string $id = null): void
|
||||
{
|
||||
$this->resetValidation();
|
||||
$this->resetErrorBag();
|
||||
|
||||
$this->method = $method;
|
||||
$this->modalTitle = $modalTitle;
|
||||
|
||||
$id && $this->form->setWarehouse(WarehouseModel::byHashOrFail($id));
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.studio.master.warehouse.index', [
|
||||
'pageTitle' => 'Gudang',
|
||||
]);
|
||||
}
|
||||
}
|
||||
91
app/Livewire/Studio/Master/Warehouse/Show.php
Normal file
91
app/Livewire/Studio/Master/Warehouse/Show.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Master\Warehouse;
|
||||
|
||||
use App\Models\Warehouse;
|
||||
use App\Traits\Authorization\WithAuthorization;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Detail Gudang')]
|
||||
class Show extends Component
|
||||
{
|
||||
use WithAuthorization;
|
||||
|
||||
public Warehouse $warehouse;
|
||||
|
||||
public function mount(Warehouse $warehouse): void
|
||||
{
|
||||
$this->warehouse = $warehouse;
|
||||
}
|
||||
|
||||
public function getStatsProperty()
|
||||
{
|
||||
$perfumesCount = $this->warehouse->perfumes()->count();
|
||||
$productsCount = $this->warehouse->products()->count();
|
||||
$bottlesCount = $this->warehouse->bottles()->count();
|
||||
$totalItems = $perfumesCount + $productsCount + $bottlesCount;
|
||||
|
||||
return [
|
||||
[
|
||||
'title' => 'Total Item',
|
||||
'value' => number_format($totalItems, 0, ',', '.'),
|
||||
],
|
||||
[
|
||||
'title' => 'Parfum',
|
||||
'value' => number_format($perfumesCount, 0, ',', '.'),
|
||||
],
|
||||
[
|
||||
'title' => 'Produk',
|
||||
'value' => number_format($productsCount, 0, ',', '.'),
|
||||
],
|
||||
[
|
||||
'title' => 'Botol',
|
||||
'value' => number_format($bottlesCount, 0, ',', '.'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getItemsProperty()
|
||||
{
|
||||
$warehouse = $this->warehouse;
|
||||
|
||||
$perfumes = $warehouse->perfumes()->get()->map(function ($item) {
|
||||
return [
|
||||
'id' => 'perfume_'.$item->id,
|
||||
'type' => 'Parfum',
|
||||
'name' => $item->name,
|
||||
'stock' => $item->pivot->stock ?? 0,
|
||||
];
|
||||
});
|
||||
|
||||
$products = $warehouse->products()->get()->map(function ($item) {
|
||||
return [
|
||||
'id' => 'product_'.$item->id,
|
||||
'type' => 'Produk',
|
||||
'name' => $item->name,
|
||||
'stock' => $item->pivot->stock ?? 0,
|
||||
];
|
||||
});
|
||||
|
||||
$bottles = $warehouse->bottles()->get()->map(function ($item) {
|
||||
return [
|
||||
'id' => 'bottle_'.$item->id,
|
||||
'type' => 'Botol',
|
||||
'name' => $item->name,
|
||||
'stock' => $item->pivot->stock ?? 0,
|
||||
];
|
||||
});
|
||||
|
||||
return $perfumes->concat($products)->concat($bottles);
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.studio.master.warehouse.show', [
|
||||
'pageTitle' => 'Detail Gudang',
|
||||
'stats' => $this->stats,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('perfume_warehouse', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('warehouse_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('perfume_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('stock')->default(0);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('perfume_warehouse');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('product_warehouse', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('warehouse_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('product_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('stock')->default(0);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('product_warehouse');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('bottle_warehouse', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('bottle_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('warehouse_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('stock')->default(0);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('bottle_warehouse');
|
||||
}
|
||||
};
|
||||
@ -14,7 +14,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<livewire:datatable.manage.warehouses-table />
|
||||
<livewire:datatable.master.warehouses-table />
|
||||
</div>
|
||||
|
||||
@include('components.modals.confirmation', [
|
||||
@ -38,7 +38,8 @@
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Deskripsi</flux:label>
|
||||
<flux:textarea placeholder="..." wire:model="form.description" autocomplete="off" />
|
||||
<flux:textarea placeholder="Gudang utama untuk penyimpanan semua produk." wire:model="form.description"
|
||||
autocomplete="off" />
|
||||
<flux:error name="form.description" />
|
||||
</flux:field>
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
<flux:main>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
|
||||
</div>
|
||||
<div>
|
||||
<flux:button href="{{ route('studio.master.warehouse.index') }}" wire:navigate class="text-sm">
|
||||
Kembali
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
@if (!empty($stats))
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||
@foreach ($stats as $stat)
|
||||
<div class="rounded-lg px-6 py-4 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:subheading>{{ $stat['title'] }}</flux:subheading>
|
||||
<flux:heading size="xl" class="mb-2">{{ $stat['value'] }}</flux:heading>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<flux:card class="mt-8">
|
||||
<div class="mb-4">
|
||||
<flux:heading size="lg">Daftar Item</flux:heading>
|
||||
<flux:subheading>Daftar semua item yang tersedia di gudang ini.</flux:subheading>
|
||||
</div>
|
||||
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.column>Tipe</flux:table.column>
|
||||
<flux:table.column>Item</flux:table.column>
|
||||
<flux:table.column>Stok</flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@forelse ($this->items as $item)
|
||||
<flux:table.row :key="$item['id']">
|
||||
<flux:table.cell>
|
||||
<flux:badge size="sm"
|
||||
:color="$item['type'] === 'Parfum' ? 'fuchsia' : ($item['type'] === 'Produk' ? 'cyan' :
|
||||
'zinc')">
|
||||
{{ $item['type'] }}</flux:badge>
|
||||
</flux:table.cell>
|
||||
<flux:table.cell class="font-medium">{{ $item['name'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ number_format($item['stock'], 0, ',', '.') }}</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@empty
|
||||
<flux:table.row>
|
||||
<flux:table.cell colspan="3" class="text-center text-zinc-500">Tidak ada item di gudang
|
||||
ini.
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforelse
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
</flux:card>
|
||||
</div>
|
||||
</flux:main>
|
||||
Loading…
Reference in New Issue
Block a user