diff --git a/app/Livewire/Datatable/Studio/Manage/FaqsTable.php b/app/Livewire/Datatable/Studio/Manage/FaqsTable.php new file mode 100644 index 0000000..2b6c8f8 --- /dev/null +++ b/app/Livewire/Datatable/Studio/Manage/FaqsTable.php @@ -0,0 +1,95 @@ +searchable(), + + Column::make('Aksi') + ->label(function ($row) { + $actions = ''; + $maxSortOrder = Faq::max('sort_order'); + + if (auth()->user()->can('update faq')) { + if ($row->sort_order > 1) { + $actions .= view('components.datatables.sort-button', [ + 'id' => $row->hash, + 'module' => 'faq', + 'direction' => 'up', + 'tooltip' => 'Naik', + 'color' => 'cyan', + 'icon' => 'arrow-up', + ])->render(); + + $actions .= view('components.datatables.sort-button', [ + 'id' => $row->hash, + 'module' => 'faq', + 'direction' => 'first', + 'tooltip' => 'Pertama', + 'color' => 'emerald', + 'icon' => 'bars-arrow-up', + ])->render(); + } + + if ($row->sort_order < $maxSortOrder) { + $actions .= view('components.datatables.sort-button', [ + 'id' => $row->hash, + 'module' => 'faq', + 'direction' => 'down', + 'tooltip' => 'Turun', + 'color' => 'orange', + 'icon' => 'arrow-down', + ])->render(); + + $actions .= view('components.datatables.sort-button', [ + 'id' => $row->hash, + 'module' => 'faq', + 'direction' => 'last', + 'tooltip' => 'Terakhir', + 'color' => 'rose', + 'icon' => 'bars-arrow-down', + ])->render(); + } + } + + if (auth()->user()->can('update faq')) { + $actions .= view('components.datatables.edit-modal', [ + 'id' => $row->hash, + 'method' => 'update', + 'modalTitle' => 'Ubah Kategori', + ])->render(); + } + + if (auth()->user()->can('delete faq')) { + $actions .= view('components.datatables.delete', [ + 'id' => $row->hash, + 'deleteRoute' => route('studio.manage.faq.delete', $row->hash), + ])->render(); + } + + return $actions; + }) + ->html(), + ]; + } + + public function builder(): Builder + { + return Faq::select('id', 'question', 'sort_order')->orderBy('sort_order'); + } +} diff --git a/app/Livewire/Forms/Studio/Manage/FaqForm.php b/app/Livewire/Forms/Studio/Manage/FaqForm.php new file mode 100644 index 0000000..4fb4cee --- /dev/null +++ b/app/Livewire/Forms/Studio/Manage/FaqForm.php @@ -0,0 +1,109 @@ + ['required', 'string', 'max:100'], + 'answer' => ['required', 'string'], + ]; + } + + public function validationAttributes(): array + { + return [ + 'question' => 'pertanyaan', + 'answer' => 'jawaban', + ]; + } + + public function setFaq(Faq $faq) + { + $this->faq = $faq; + + $this->question = $faq->question; + $this->answer = $faq->answer; + } + + public function store() + { + $this->validate(); + + Faq::create([ + 'question' => $this->question, + 'answer' => $this->answer, + 'sort_order' => Faq::count() + 1, + ]); + } + + public function update() + { + $this->validate(); + + $this->faq->update([ + 'question' => $this->question, + 'answer' => $this->answer, + ]); + } + + public function delete() + { + $this->faq->delete(); + + Faq::where('sort_order', '>', $this->faq->sort_order) + ->orderBy('sort_order') + ->get() + ->each(function ($cat) { + $cat->update(['sort_order' => $cat->sort_order - 1]); + }); + } + + public function sortOrder(string $direction) + { + if (in_array($direction, ['up', 'down'])) { + $swap = null; + + if ($direction === 'up') { + $swap = Faq::where('sort_order', '<', $this->faq->sort_order) + ->orderByDesc('sort_order') + ->first(); + } else { + $swap = Faq::where('sort_order', '>', $this->faq->sort_order) + ->orderBy('sort_order') + ->first(); + } + + if ($swap) { + $temp = $this->faq->sort_order; + $this->faq->update(['sort_order' => $swap->sort_order]); + $swap->update(['sort_order' => $temp]); + } + } elseif ($direction === 'first') { + $this->faq->update(['sort_order' => Faq::min('sort_order') - 1]); + $this->normalizeSortOrder(); + } elseif ($direction === 'last') { + $this->faq->update(['sort_order' => Faq::max('sort_order') + 1]); + $this->normalizeSortOrder(); + } + } + + protected function normalizeSortOrder() + { + $categories = Faq::orderBy('sort_order')->get(); + foreach ($categories as $index => $faq) { + $faq->update(['sort_order' => $index + 1]); + } + } +} diff --git a/app/Livewire/Studio/Manage/Faq.php b/app/Livewire/Studio/Manage/Faq.php new file mode 100644 index 0000000..d9ebbcf --- /dev/null +++ b/app/Livewire/Studio/Manage/Faq.php @@ -0,0 +1,99 @@ +resetValidation(); + $this->resetErrorBag(); + + $this->method = $method; + $this->modalTitle = $modalTitle; + + if ($id) { + $this->form->setFaq(FaqModel::byHashOrFail($id)); + } + } + + public function create() + { + $this->canOrAbort('create faq'); + + $this->form->store(); + + $this->dispatch('refreshDatatable'); + + $this->toast('FAQs berhasil ditambahkan.'); + + Flux::modals()->close(); + } + + public function update() + { + $this->canOrAbort('update faq'); + + $this->form->update(); + + $this->dispatch('refreshDatatable'); + + $this->toast('FAQs berhasil diperbarui.'); + + Flux::modals()->close(); + } + + public function delete(FaqModel $faq) + { + $this->form->faq = $faq; + + $this->form->delete(); + + $this->dispatch('refreshDatatable'); + + $this->toast('FAQs berhasil dihapus.'); + + Flux::modals()->close(); + } + + #[On('fn:sortOrder')] + public function sortOrder(FaqModel $faq, string $direction) + { + $this->canOrAbort('update faq'); + + $this->form->faq = $faq; + + $this->form->sortOrder($direction); + + $this->dispatch('refreshDatatable'); + } + + public function render() + { + return view('livewire.studio.manage.faqs', [ + 'pageTitle' => 'FAQs', + ]); + } +} diff --git a/app/Models/Faq.php b/app/Models/Faq.php new file mode 100644 index 0000000..0a7767a --- /dev/null +++ b/app/Models/Faq.php @@ -0,0 +1,21 @@ + 'int', + ]; + } +} diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index d1d46b8..0af0580 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -158,6 +158,13 @@ public function boot(): void 'match' => 'studio.manage.article.*', 'can' => 'view article', ], + [ + 'label' => 'FAQs', + 'icon' => 'question-mark-circle', + 'route' => 'studio.manage.faq.index', + 'match' => 'studio.manage.faq.*', + 'can' => 'view faq', + ], ], ], [ diff --git a/database/migrations/2025_11_06_084400_create_faqs_table.php b/database/migrations/2025_11_06_084400_create_faqs_table.php new file mode 100644 index 0000000..30b8c7f --- /dev/null +++ b/database/migrations/2025_11_06_084400_create_faqs_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('question', 100); + $table->text('answer'); + $table->unsignedInteger('sort_order'); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('faqs'); + } +}; diff --git a/database/seeders/RolePermissionSeeder.php b/database/seeders/RolePermissionSeeder.php index a145574..a41221a 100644 --- a/database/seeders/RolePermissionSeeder.php +++ b/database/seeders/RolePermissionSeeder.php @@ -106,14 +106,19 @@ public function run(): void 'delete order', 'show order', + 'view purchase', + 'create purchase', + 'delete purchase', + 'view article', 'create article', 'update article', 'delete article', - 'view purchase', - 'create purchase', - 'delete purchase', + 'view faq', + 'create faq', + 'update faq', + 'delete faq', 'view application settings', diff --git a/resources/views/livewire/studio/manage/faqs.blade.php b/resources/views/livewire/studio/manage/faqs.blade.php new file mode 100644 index 0000000..d33d035 --- /dev/null +++ b/resources/views/livewire/studio/manage/faqs.blade.php @@ -0,0 +1,47 @@ + +
+
+ {{ $pageTitle }} +
+ @can('create faq') +
+ + Tambah + + +
+ @endcan +
+ +
+ +
+ + @include('components.confirmation.delete') + +
+ {{ $modalTitle }} + + + Pertanyaan * + + + + + + Jawaban * + + + +
+ + + Simpan + +
+
+
+
diff --git a/routes/pages/studio.php b/routes/pages/studio.php index 2b46ef2..416b346 100644 --- a/routes/pages/studio.php +++ b/routes/pages/studio.php @@ -26,6 +26,7 @@ use App\Livewire\Studio\Manage\Article\Create as ArticleCreate; use App\Livewire\Studio\Manage\Article\Edit as ArticleEdit; use App\Livewire\Studio\Manage\Article\Index as ArticleIndex; +use App\Livewire\Studio\Manage\Faq as FaqComponent; use App\Livewire\Studio\Manage\Order\Create as OrderCreate; use App\Livewire\Studio\Manage\Order\Index as OrderIndex; use App\Livewire\Studio\Manage\Order\Show as OrderShow; @@ -166,6 +167,11 @@ Route::get('/{article}/edit', ArticleEdit::class)->name('edit')->middleware('can:update article'); Route::delete('/{article}/delete', ArticleCreate::class)->name('delete')->middleware('can:delete article'); }); + + Route::prefix('faqs')->name('faq.')->group(function () { + Route::get('/', FaqComponent::class)->name('index')->middleware('can:view faq'); + Route::delete('/{faq}/delete', FaqComponent::class)->name('delete')->middleware('can:delete faq'); + }); }); Route::get('/settings/applications', ApplicationComponent::class)->name('studio.setting.application')->middleware('can:view application settings');