diff --git a/app/Livewire/Datatable/MembershipsTable.php b/app/Livewire/Datatable/MembershipsTable.php
new file mode 100644
index 0000000..b1d7b26
--- /dev/null
+++ b/app/Livewire/Datatable/MembershipsTable.php
@@ -0,0 +1,59 @@
+searchable(),
+
+ Column::make('Min. Pembelian', 'min_purchase')
+ ->format(fn ($value) => currency($value, 'Rp'))
+ ->searchable()
+ ->sortable(),
+
+ Column::make('Diskon', 'discount_percentage')
+ ->format(fn ($value) => percentage($value))
+ ->searchable()
+ ->sortable(),
+
+ Column::make('Aksi')
+ ->label(function ($row) {
+ $actions = '';
+
+ $actions .= view('components.datatables.edit-modal', [
+ 'id' => $row->id,
+ 'method' => 'update',
+ 'modalTitle' => 'Ubah Membership',
+ ])->render();
+
+ $actions .= view('components.datatables.delete', [
+ 'id' => $row->id,
+ 'deleteRoute' => route('studio.loyalty.membership.delete', $row->id),
+ ])->render();
+
+ return $actions;
+ })
+ ->html(),
+ ];
+ }
+
+ public function builder(): Builder
+ {
+ return Membership::select('id', 'name', 'discount_percentage', 'min_purchase');
+ }
+}
diff --git a/app/Livewire/Forms/MembershipForm.php b/app/Livewire/Forms/MembershipForm.php
new file mode 100644
index 0000000..7957642
--- /dev/null
+++ b/app/Livewire/Forms/MembershipForm.php
@@ -0,0 +1,73 @@
+ ['required', 'string', 'max:50'],
+ 'min_purchase' => ['required', 'regex:/^[0-9.]+$/', 'max:99999999999'],
+ 'discount_percentage' => ['required', 'numeric', 'between:0,100'],
+ 'terms_conditions' => ['nullable', 'string'],
+ ];
+ }
+
+ public function validationAttributes(): array
+ {
+ return [
+ 'name' => 'nama',
+ 'min_purchase' => 'min. pembelian',
+ 'discount_percentage' => 'persentase diskon',
+ 'terms_conditions' => 'syarat dan ketentuan',
+ ];
+ }
+
+ public function setMembership(Membership $membership)
+ {
+ $this->membership = $membership;
+
+ $this->name = $membership->name;
+ $this->min_purchase = currency($membership->min_purchase);
+ $this->terms_conditions = $membership->terms_conditions;
+ $this->discount_percentage = $membership->discount_percentage;
+ }
+
+ public function store()
+ {
+ $this->validate();
+
+ Membership::create([
+ 'name' => $this->name,
+ 'min_purchase' => replaceCurrency($this->min_purchase),
+ 'discount_percentage' => $this->discount_percentage,
+ 'terms_conditions' => $this->terms_conditions,
+ ]);
+ }
+
+ public function update()
+ {
+ $this->validate();
+
+ $this->membership->update([
+ 'name' => $this->name,
+ 'min_purchase' => replaceCurrency($this->min_purchase),
+ 'discount_percentage' => $this->discount_percentage,
+ 'terms_conditions' => $this->terms_conditions,
+ ]);
+ }
+}
diff --git a/app/Livewire/Studio/Loyalty/Membership.php b/app/Livewire/Studio/Loyalty/Membership.php
new file mode 100644
index 0000000..4f9b55b
--- /dev/null
+++ b/app/Livewire/Studio/Loyalty/Membership.php
@@ -0,0 +1,93 @@
+resetValidation();
+ $this->resetErrorBag();
+
+ $this->method = $method;
+ $this->modalTitle = $modalTitle;
+
+ if ($id) {
+ $this->form->setMembership(MembershipModel::findOrFail($id));
+ }
+ }
+
+ public function create()
+ {
+ $this->form->store();
+
+ $this->dispatch('refreshDatatable');
+
+ Flux::toast(
+ heading: 'Berhasil',
+ text: 'Membership berhasil ditambahkan.',
+ variant: 'success',
+ duration: 3000
+ );
+
+ Flux::modals()->close();
+ }
+
+ public function update()
+ {
+ $this->form->update();
+
+ $this->dispatch('refreshDatatable');
+
+ Flux::toast(
+ heading: 'Berhasil',
+ text: 'Membership berhasil diperbarui.',
+ variant: 'success',
+ duration: 3000
+ );
+
+ Flux::modals()->close();
+ }
+
+ public function delete(MembershipModel $membership)
+ {
+ $membership->delete();
+
+ $this->dispatch('refreshDatatable');
+
+ Flux::toast(
+ heading: 'Berhasil',
+ text: 'Membership berhasil dihapus.',
+ variant: 'success',
+ );
+
+ Flux::modals()->close();
+ }
+
+ public function render()
+ {
+ return view('livewire.studio.loyalty.memberships', [
+ 'pageTitle' => 'Membership',
+ ]);
+ }
+}
diff --git a/app/Models/Membership.php b/app/Models/Membership.php
new file mode 100644
index 0000000..f41246b
--- /dev/null
+++ b/app/Models/Membership.php
@@ -0,0 +1,22 @@
+ 'int',
+ 'discount_percent' => 'decimal:2',
+ ];
+ }
+}
diff --git a/database/migrations/2025_09_24_141358_create_memberships_table.php b/database/migrations/2025_09_24_141358_create_memberships_table.php
new file mode 100644
index 0000000..a0dbd40
--- /dev/null
+++ b/database/migrations/2025_09_24_141358_create_memberships_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->string('name', 50);
+ $table->text('terms_conditions')->nullable();
+ $table->unsignedInteger('min_purchase');
+ $table->decimal('discount_percentage', 5, 2);
+ $table->timestamps();
+ $table->softDeletes();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('memberships');
+ }
+};
diff --git a/resources/views/components/datatables/edit-modal.blade.php b/resources/views/components/datatables/edit-modal.blade.php
new file mode 100644
index 0000000..35ede06
--- /dev/null
+++ b/resources/views/components/datatables/edit-modal.blade.php
@@ -0,0 +1,7 @@
+