feat(warehouse): implementasi crud gudang
This commit is contained in:
parent
3c1b6fc915
commit
d239d58029
53
app/Livewire/Datatable/Manage/WarehousesTable.php
Normal file
53
app/Livewire/Datatable/Manage/WarehousesTable.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Datatable\Manage;
|
||||
|
||||
use App\Models\Warehouse;
|
||||
use App\Traits\Datatable\WithAppendColumn;
|
||||
use App\Traits\Datatable\WithConfiguration;
|
||||
use App\Traits\Datatable\WithPrependColumn;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
||||
use Rappasoft\LaravelLivewireTables\Views\Column;
|
||||
|
||||
class WarehousesTable extends DataTableComponent
|
||||
{
|
||||
use WithAppendColumn, WithConfiguration, WithPrependColumn;
|
||||
|
||||
protected $model = Warehouse::class;
|
||||
|
||||
public function columns(): array
|
||||
{
|
||||
return [
|
||||
Column::make('Nama', 'name')->searchable()->sortable(),
|
||||
|
||||
Column::make('Aksi')
|
||||
->label(function ($row) {
|
||||
$actions = '';
|
||||
|
||||
if (auth()->user()->can('update warehouse')) {
|
||||
$actions .= view('components.actions.table.edit-modal', [
|
||||
'id' => $row->hash,
|
||||
'method' => 'update',
|
||||
'modalTitle' => 'Ubah Gudang',
|
||||
])->render();
|
||||
}
|
||||
|
||||
if (auth()->user()->can('delete warehouse')) {
|
||||
$actions .= view('components.actions.table.delete', [
|
||||
'id' => $row->hash,
|
||||
])->render();
|
||||
}
|
||||
|
||||
return $actions;
|
||||
})
|
||||
->html()
|
||||
->hideIf(auth()->user()->cannot('update warehouse') && auth()->user()->cannot('delete warehouse')),
|
||||
];
|
||||
}
|
||||
|
||||
public function builder(): Builder
|
||||
{
|
||||
return Warehouse::select('id', 'name');
|
||||
}
|
||||
}
|
||||
59
app/Livewire/Forms/Studio/Master/WarehouseForm.php
Normal file
59
app/Livewire/Forms/Studio/Master/WarehouseForm.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Forms\Studio\Master;
|
||||
|
||||
use App\Models\Warehouse;
|
||||
use Livewire\Form;
|
||||
|
||||
class WarehouseForm extends Form
|
||||
{
|
||||
public ?Warehouse $warehouse;
|
||||
|
||||
public string $name = '';
|
||||
|
||||
public ?string $description = null;
|
||||
|
||||
public function setWarehouse(Warehouse $warehouse): void
|
||||
{
|
||||
$this->warehouse = $warehouse;
|
||||
$this->name = $warehouse->name;
|
||||
$this->description = $warehouse->description;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:100'],
|
||||
'description' => 'nullable',
|
||||
];
|
||||
}
|
||||
|
||||
public function validationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'nama',
|
||||
'description' => 'keterangan',
|
||||
];
|
||||
}
|
||||
|
||||
public function store(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
Warehouse::create($this->except('warehouse'));
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function update(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->warehouse->update($this->except('warehouse'));
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
$this->warehouse->delete();
|
||||
}
|
||||
}
|
||||
89
app/Livewire/Studio/Master/Warehouse.php
Normal file
89
app/Livewire/Studio/Master/Warehouse.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Master;
|
||||
|
||||
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 Warehouse 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', [
|
||||
'pageTitle' => 'Gudang',
|
||||
]);
|
||||
}
|
||||
}
|
||||
45
app/Models/Warehouse.php
Normal file
45
app/Models/Warehouse.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Dyrynda\Database\Support\CascadeSoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Veelasky\LaravelHashId\Eloquent\HashableId;
|
||||
|
||||
class Warehouse extends Model
|
||||
{
|
||||
use CascadeSoftDeletes, HasFactory, HashableId, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $cascadeDeletes = ['perfumes', 'products', 'bottles', 'purchases', 'restocks'];
|
||||
|
||||
public function perfumes(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Perfume::class)->withPivot('stock');
|
||||
}
|
||||
|
||||
public function products(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Product::class)->withPivot('stock');
|
||||
}
|
||||
|
||||
public function bottles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Bottle::class)->withPivot('stock');
|
||||
}
|
||||
|
||||
public function purchases(): HasMany
|
||||
{
|
||||
return $this->hasMany(Purchase::class);
|
||||
}
|
||||
|
||||
public function restocks(): HasMany
|
||||
{
|
||||
return $this->hasMany(Restock::class);
|
||||
}
|
||||
}
|
||||
@ -58,6 +58,13 @@ public function boot(): void
|
||||
'match' => 'studio.master.outlet.*',
|
||||
'can' => 'view outlet',
|
||||
],
|
||||
[
|
||||
'label' => 'Gudang',
|
||||
'icon' => 'building-office-2',
|
||||
'route' => 'studio.master.warehouse.index',
|
||||
'match' => 'studio.master.warehouse.*',
|
||||
'can' => 'view warehouse',
|
||||
],
|
||||
[
|
||||
'label' => 'Pegawai',
|
||||
'icon' => 'users',
|
||||
@ -190,6 +197,13 @@ public function boot(): void
|
||||
'match' => 'studio.manage.purchase.*',
|
||||
'can' => 'view purchase',
|
||||
],
|
||||
[
|
||||
'label' => 'Restock Toko',
|
||||
'icon' => 'arrows-right-left',
|
||||
'route' => 'studio.manage.restock.index',
|
||||
'match' => 'studio.manage.restock.*',
|
||||
'can' => 'view restock',
|
||||
],
|
||||
[
|
||||
'label' => 'Stock Opname',
|
||||
'icon' => 'clipboard-document-check',
|
||||
|
||||
27
database/factories/WarehouseFactory.php
Normal file
27
database/factories/WarehouseFactory.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Warehouse;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Warehouse>
|
||||
*/
|
||||
class WarehouseFactory extends Factory
|
||||
{
|
||||
protected $model = Warehouse::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->company().' Warehouse',
|
||||
'description' => fake()->optional()->sentence(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -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('warehouses', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 100);
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('warehouses');
|
||||
}
|
||||
};
|
||||
@ -11,6 +11,7 @@ public function run(): void
|
||||
if (app()->environment(['staging', 'production'])) {
|
||||
$this->call([
|
||||
RolePermissionSeeder::class,
|
||||
WarehouseSeeder::class,
|
||||
OutletSeeder::class,
|
||||
UserSeeder::class,
|
||||
TierSeeder::class,
|
||||
@ -19,6 +20,7 @@ public function run(): void
|
||||
$this->call([
|
||||
RolePermissionSeeder::class,
|
||||
OutletSeeder::class,
|
||||
WarehouseSeeder::class,
|
||||
UserSeeder::class,
|
||||
TierSeeder::class,
|
||||
VoucherSeeder::class,
|
||||
|
||||
@ -146,6 +146,16 @@ public function run(): void
|
||||
'create broadcast',
|
||||
'delete broadcast',
|
||||
|
||||
'view warehouse',
|
||||
'create warehouse',
|
||||
'update warehouse',
|
||||
'delete warehouse',
|
||||
|
||||
'view restock',
|
||||
'create restock',
|
||||
'update restock',
|
||||
'delete restock',
|
||||
|
||||
'view perfume feature',
|
||||
'create perfume feature',
|
||||
'update perfume feature',
|
||||
|
||||
20
database/seeders/WarehouseSeeder.php
Normal file
20
database/seeders/WarehouseSeeder.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Warehouse;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class WarehouseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Warehouse::create([
|
||||
'name' => 'Gudang Pusat',
|
||||
'description' => 'Gudang utama untuk penyimpanan semua produk.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
53
resources/views/livewire/studio/master/warehouse.blade.php
Normal file
53
resources/views/livewire/studio/master/warehouse.blade.php
Normal file
@ -0,0 +1,53 @@
|
||||
<flux:main>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
|
||||
</div>
|
||||
@can('create warehouse')
|
||||
<flux:modal.trigger name="form-modal">
|
||||
<flux:button variant="primary" class="text-sm"
|
||||
wire:click="$dispatch('modal:open', {method: 'create', 'modalTitle': 'Tambah Gudang'})">
|
||||
Tambah
|
||||
</flux:button>
|
||||
</flux:modal.trigger>
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<livewire:datatable.manage.warehouses-table />
|
||||
</div>
|
||||
|
||||
@include('components.modals.confirmation', [
|
||||
'modalName' => 'delete-confirmation',
|
||||
'modalTitle' => 'Apakah Anda yakin?',
|
||||
'modalMessage' => 'Data yang berelasi dengan data ini juga akan ikut terhapus.',
|
||||
'buttonVariant' => 'primary',
|
||||
'buttonColor' => 'danger',
|
||||
'buttonText' => 'Ya, Hapus',
|
||||
])
|
||||
|
||||
<flux:modal name="form-modal" class="w-[95%] max-w-sm md:max-w-xl mx-auto" @close="closeModal('form-modal')">
|
||||
<div class="p-4 space-y-6">
|
||||
<flux:heading size="lg">{{ $modalTitle }}</flux:heading>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Nama <span class="text-red-500 ms-1">*</span></flux:label>
|
||||
<flux:input placeholder="Gudang Pusat" wire:model="form.name" autofocus autocomplete="off" />
|
||||
<flux:error name="form.name" />
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>Deskripsi</flux:label>
|
||||
<flux:textarea placeholder="..." wire:model="form.description" autocomplete="off" />
|
||||
<flux:error name="form.description" />
|
||||
</flux:field>
|
||||
|
||||
<div class="flex">
|
||||
<flux:spacer />
|
||||
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="{{ $method }}">
|
||||
Simpan
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</flux:modal>
|
||||
</flux:main>
|
||||
@ -50,6 +50,7 @@
|
||||
use App\Livewire\Studio\Master\User\Create as UserCreate;
|
||||
use App\Livewire\Studio\Master\User\Edit as UserEdit;
|
||||
use App\Livewire\Studio\Master\User\Index as UserIndex;
|
||||
use App\Livewire\Studio\Master\Warehouse as WarehouseComponent;
|
||||
use App\Livewire\Studio\Setting\Account as AccountComponent;
|
||||
use App\Livewire\Studio\Setting\Application as ApplicationComponent;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@ -72,6 +73,10 @@
|
||||
Route::get('/{outlet}/edit', OutletEdit::class)->name('edit')->middleware('can:update outlet');
|
||||
});
|
||||
|
||||
Route::prefix('warehouse')->name('warehouse.')->group(function () {
|
||||
Route::get('/', WarehouseComponent::class)->name('index')->middleware('can:view warehouse');
|
||||
});
|
||||
|
||||
Route::prefix('users')->name('user.')->group(function () {
|
||||
Route::get('/', UserIndex::class)->name('index')->middleware('can:view user');
|
||||
Route::get('/create', UserCreate::class)->name('create')->middleware('can:create user');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user