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.*',
'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',
'delete user',
'view formula',
'create formula',
'update formula',
'delete formula',
'view tier',
'create 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\Purchase\Create as PurchaseCreate;
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\Edit as OutletEdit;
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::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 () {