feat(formula): membuat crud formula

This commit is contained in:
Yoga Pangestu 2025-10-06 20:16:53 +07:00
parent 7ac13a5f13
commit 7594e7abac
9 changed files with 362 additions and 0 deletions

View File

@ -0,0 +1,63 @@
<?php
namespace App\Livewire\Datatable\Studio\Master;
use App\Models\Formula;
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 FormulasTable extends DataTableComponent
{
use WithAppendColumn, WithConfiguration, WithPrependColumn;
protected $model = Formula::class;
public function columns(): array
{
return [
Column::make('Kualitas', 'quality')->searchable(),
Column::make('Ukuran', 'size')
->format(fn ($row, $column) => $row.' ml')
->searchable()
->sortable(),
Column::make('Volume', 'volume')
->format(fn ($row, $column) => $row.' ml')
->searchable()
->sortable(),
Column::make('Aksi')
->label(function ($row) {
$actions = '';
if (auth()->user()->can('update formula')) {
$actions .= view('components.datatables.edit-modal', [
'id' => $row->hash,
'method' => 'update',
'modalTitle' => 'Ubah Rumus',
])->render();
}
if (auth()->user()->can('delete formula')) {
$actions .= view('components.datatables.delete', [
'id' => $row->hash,
'deleteRoute' => route('studio.master.formula.delete', $row->hash),
])->render();
}
return $actions;
})
->html(),
];
}
public function builder(): Builder
{
return Formula::select('id', 'quality', 'size', 'volume');
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Livewire\Forms\Studio\Master;
use App\Models\Formula;
use App\Rules\UnsignedInteger;
use Livewire\Form;
class FormulaForm extends Form
{
public ?Formula $formula = null;
public string $quality = '';
public string $size = '';
public string $volume = '';
public function rules(): array
{
return [
'quality' => ['required', 'string', 'max:20'],
'size' => ['required', 'numeric', new UnsignedInteger],
'volume' => ['required', 'numeric', new UnsignedInteger],
];
}
public function validationAttributes(): array
{
return [
'quality' => 'kualitas',
'size' => 'ukuran',
];
}
public function setFormula(Formula $formula)
{
$this->formula = $formula;
$this->quality = $formula->quality;
$this->size = $formula->size;
$this->volume = $formula->volume;
}
public function store()
{
$this->validate();
Formula::create($this->prepareSavedData());
}
public function update()
{
$this->validate();
$this->formula->update($this->prepareSavedData());
}
private function prepareSavedData()
{
return [
'quality' => $this->quality,
'size' => $this->size,
'volume' => $this->volume,
];
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace App\Livewire\Studio\Master;
use App\Livewire\Forms\Studio\Master\FormulaForm;
use App\Models\Bottle;
use App\Models\Formula as FormulaModel;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation;
use App\Traits\WithToast;
use App\Traits\WithUpdatedData;
use Flux\Flux;
use Livewire\Attributes\On;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Title('Rumus')]
class Formula extends Component
{
use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public FormulaForm $form;
public string $method = 'create';
public string $modalTitle = '';
public array $qualities = ['Premium', 'Super Premium', 'Murni'];
public array $sizes = [];
public function mount()
{
$this->sizes = Bottle::distinct()->orderBy('size')->pluck('size')->toArray();
}
#[On('modal:open')]
public function openModal(string $method, string $modalTitle, ?string $id = null)
{
$this->resetValidation();
$this->resetErrorBag();
$this->method = $method;
$this->modalTitle = $modalTitle;
if ($id) {
$this->form->setFormula(FormulaModel::byHashOrFail($id));
}
}
public function create()
{
$this->canOrAbort('create formula');
$this->form->store();
$this->dispatch('refreshDatatable');
$this->toast('Rumus berhasil ditambahkan.');
Flux::modals()->close();
}
public function update()
{
$this->canOrAbort('update formula');
$this->form->update();
$this->dispatch('refreshDatatable');
$this->toast('Rumus berhasil diperbarui.');
Flux::modals()->close();
}
public function delete(FormulaModel $formula)
{
$formula->delete();
$this->dispatch('refreshDatatable');
$this->toast('Rumus berhasil dihapus.');
Flux::modals()->close();
}
public function render()
{
return view('livewire.studio.master.formulas', [
'pageTitle' => 'Rumus',
]);
}
}

23
app/Models/Formula.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Veelasky\LaravelHashId\Eloquent\HashableId;
class Formula extends Model
{
use HasFactory, HashableId, SoftDeletes;
protected $guarded = ['id'];
protected function casts(): array
{
return [
'size' => 'int',
'volume' => 'int',
];
}
}

View File

@ -40,6 +40,13 @@ public function boot(): void
'match' => 'studio.master.user.*', 'match' => 'studio.master.user.*',
'can' => 'view user', 'can' => 'view user',
], ],
[
'label' => 'Rumus',
'icon' => 'variable',
'route' => 'studio.master.formula.index',
'match' => 'studio.master.formula.*',
'can' => 'view formula',
],
], ],
], ],
[ [

View File

@ -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('formulas', function (Blueprint $table) {
$table->id();
$table->string('quality', 20);
$table->unsignedInteger('size');
$table->unsignedInteger('volume');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('formulas');
}
};

View File

@ -39,6 +39,11 @@ public function run(): void
'update user', 'update user',
'delete user', 'delete user',
'view formula',
'create formula',
'update formula',
'delete formula',
'view tier', 'view tier',
'create tier', 'create tier',
'update tier', 'update tier',

View File

@ -0,0 +1,65 @@
<flux:main>
<div class="flex justify-between items-center">
<div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div>
@if (auth()->user()->can('create formula'))
<div>
<flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm"
wire:click="$dispatch('modal:open', {method: 'create', 'modalTitle': 'Tambah Rumus'})">Tambah
</flux:button>
</flux:modal.trigger>
</div>
@endif
</div>
<div class="mt-6">
<livewire:datatable.studio.master.formulas-table />
</div>
@include('components.confirmation.delete')
<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>Kualitas <span class="text-red-500 ms-1">*</span></flux:label>
<flux:select variant="listbox" searchable placeholder="Pilih Kualitas"
wire:model.live.debounce.500ms="form.quality">
@foreach ($qualities as $key => $value)
<flux:select.option value="{{ $value }}">{{ $value }}
</flux:select.option>
@endforeach
</flux:select>
<flux:error name="form.quality" />
</flux:field>
<flux:field>
<flux:label>Ukuran <span class="text-red-500 ms-1">*</span></flux:label>
<flux:select variant="listbox" searchable placeholder="Pilih Ukuran"
wire:model.live.debounce.500ms="form.size">
@foreach ($sizes as $key => $value)
<flux:select.option value="{{ $value }}">{{ $value }} ml
</flux:select.option>
@endforeach
</flux:select>
<flux:error name="form.size" />
</flux:field>
<flux:field>
<flux:label>Volume <span class="text-red-500 ms-1">*</span></flux:label>
<flux:input placeholder="Masukkan volume" wire:model.live.debounce.500ms="form.volume"
autocomplete="off" />
<flux:error name="form.volume" />
</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>

View File

@ -24,6 +24,7 @@
use App\Livewire\Studio\Manage\Article\Index as ArticleIndex; use App\Livewire\Studio\Manage\Article\Index as ArticleIndex;
use App\Livewire\Studio\Manage\Purchase\Create as PurchaseCreate; use App\Livewire\Studio\Manage\Purchase\Create as PurchaseCreate;
use App\Livewire\Studio\Manage\Purchase\Index as PurchaseIndex; use App\Livewire\Studio\Manage\Purchase\Index as PurchaseIndex;
use App\Livewire\Studio\Master\Formula as FormulaComponent;
use App\Livewire\Studio\Master\Outlet\Create as OutletCreate; use App\Livewire\Studio\Master\Outlet\Create as OutletCreate;
use App\Livewire\Studio\Master\Outlet\Edit as OutletEdit; use App\Livewire\Studio\Master\Outlet\Edit as OutletEdit;
use App\Livewire\Studio\Master\Outlet\Index as OutletIndex; use App\Livewire\Studio\Master\Outlet\Index as OutletIndex;
@ -60,6 +61,11 @@
Route::get('/{user}/edit', UserEdit::class)->name('edit')->middleware('can:update user'); Route::get('/{user}/edit', UserEdit::class)->name('edit')->middleware('can:update user');
Route::delete('/{user}/delete', UserCreate::class)->name('delete')->middleware('can:delete user'); Route::delete('/{user}/delete', UserCreate::class)->name('delete')->middleware('can:delete user');
}); });
Route::prefix('formulas')->name('formula.')->group(function () {
Route::get('/', FormulaComponent::class)->name('index')->middleware('can:view formula');
Route::delete('/{formula}/delete', FormulaComponent::class)->name('delete')->middleware('can:delete formula');
});
}); });
Route::prefix('loyalty')->name('studio.loyalty.')->group(function () { Route::prefix('loyalty')->name('studio.loyalty.')->group(function () {