feat(warehouse): membuat fitur gudang
This commit is contained in:
parent
a2efc0f349
commit
77babcf44c
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Datatable\Manage;
|
namespace App\Livewire\Datatable\Master;
|
||||||
|
|
||||||
use App\Models\Warehouse;
|
use App\Models\Warehouse;
|
||||||
use App\Traits\Datatable\WithAppendColumn;
|
use App\Traits\Datatable\WithAppendColumn;
|
||||||
@ -25,6 +25,12 @@ public function columns(): array
|
|||||||
->label(function ($row) {
|
->label(function ($row) {
|
||||||
$actions = '';
|
$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')) {
|
if (auth()->user()->can('update warehouse')) {
|
||||||
$actions .= view('components.actions.table.edit-modal', [
|
$actions .= view('components.actions.table.edit-modal', [
|
||||||
'id' => $row->hash,
|
'id' => $row->hash,
|
||||||
@ -42,7 +48,7 @@ public function columns(): array
|
|||||||
return $actions;
|
return $actions;
|
||||||
})
|
})
|
||||||
->html()
|
->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>
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<livewire:datatable.manage.warehouses-table />
|
<livewire:datatable.master.warehouses-table />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('components.modals.confirmation', [
|
@include('components.modals.confirmation', [
|
||||||
@ -38,7 +38,8 @@
|
|||||||
|
|
||||||
<flux:field>
|
<flux:field>
|
||||||
<flux:label>Deskripsi</flux:label>
|
<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:error name="form.description" />
|
||||||
</flux:field>
|
</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